src/messaging/win32wce/qmailmessage.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 "qmailmessage_p.h"
       
    43 #include "qmailaddress.h"
       
    44 #include "qmailcodec.h"
       
    45 #include "qmaillog.h"
       
    46 #include "qmailnamespace.h"
       
    47 #include "qmailtimestamp.h"
       
    48 #include "longstring_p.h"
       
    49 
       
    50 #ifndef QTOPIAMAIL_PARSING_ONLY
       
    51 #include "qmailaccount.h"
       
    52 #include "qmailfolder.h"
       
    53 #include "qmailstore.h"
       
    54 #endif
       
    55 
       
    56 #include <qcryptographichash.h>
       
    57 #include <qdir.h>
       
    58 #include <qfile.h>
       
    59 #include <qfileinfo.h>
       
    60 #include <qregexp.h>
       
    61 #include <qtextstream.h>
       
    62 #include <qtextcodec.h>
       
    63 #include <QTextCodec>
       
    64 #include <QtDebug>
       
    65 
       
    66 #include <stdlib.h>
       
    67 #include <limits.h>
       
    68 #if defined(Q_OS_WIN) && defined(_WIN32_WCE)
       
    69 #include <cctype>
       
    70 #else
       
    71 #include <ctype.h>
       
    72 #endif
       
    73 
       
    74 static const QByteArray internalPrefix()
       
    75 {
       
    76     static const QByteArray prefix("X-qtopiamail-internal-");
       
    77     return prefix;
       
    78 }
       
    79 
       
    80 template<typename CharType>
       
    81 inline char toPlainChar(CharType value) { return value; }
       
    82 
       
    83 template<>
       
    84 inline char toPlainChar<QChar>(QChar value) { return static_cast<char>(value.unicode() & 0x7f); }
       
    85 
       
    86 template<typename CharType>
       
    87 inline bool asciiRepresentable(const CharType& value) { return ((value <= 127) && (value >= 0)); }
       
    88 
       
    89 template<>
       
    90 inline bool asciiRepresentable<unsigned char>(const unsigned char& value) { return (value <= 127); }
       
    91 
       
    92 template<>
       
    93 inline bool asciiRepresentable<signed char>(const signed char& value) { return (value >= 0); }
       
    94 
       
    95 // The correct test for char depends on whether the platform defines char as signed or unsigned
       
    96 // Default to signed char:
       
    97 template<bool SignedChar>
       
    98 inline bool asciiRepresentableChar(const char& value) { return asciiRepresentable(static_cast<signed char>(value)); }
       
    99 
       
   100 template<>
       
   101 inline bool asciiRepresentableChar<false>(const char& value) { return asciiRepresentable(static_cast<unsigned char>(value)); }
       
   102 
       
   103 template<>
       
   104 inline bool asciiRepresentable<char>(const char& value) { return asciiRepresentableChar<(SCHAR_MIN < CHAR_MIN)>(value); }
       
   105 
       
   106 template<typename StringType>
       
   107 QByteArray to7BitAscii(const StringType& src)
       
   108 {
       
   109     QByteArray result;
       
   110     result.reserve(src.length());
       
   111 
       
   112     typename StringType::const_iterator it = src.begin();
       
   113     for (const typename StringType::const_iterator end = it + src.length(); it != end; ++it)
       
   114         if (asciiRepresentable(*it))
       
   115             result.append(toPlainChar(*it));
       
   116 
       
   117     return result;
       
   118 }
       
   119 
       
   120 
       
   121 // Parsing functions
       
   122 static int insensitiveIndexOf(const QByteArray& content, const QByteArray& container, int from = 0)
       
   123 {
       
   124     const char* const matchBegin = content.constData();
       
   125     const char* const matchEnd = matchBegin + content.length();
       
   126 
       
   127     const char* const begin = container.constData();
       
   128     const char* const end = begin + container.length() - (content.length() - 1);
       
   129 
       
   130     const char* it = begin + from;
       
   131     while (it < end)
       
   132     {
       
   133         if (toupper(*it++) == toupper(*matchBegin))
       
   134         {
       
   135             const char* restart = it;
       
   136 
       
   137             // See if the remainder matches
       
   138             const char* searchIt = it;
       
   139             const char* matchIt = matchBegin + 1;
       
   140 
       
   141             do 
       
   142             {
       
   143                 if (matchIt == matchEnd)
       
   144                     return ((it - 1) - begin);
       
   145 
       
   146                 // We may find the next place to search in our scan
       
   147                 if ((restart == it) && (*searchIt == *(it - 1)))
       
   148                     restart = searchIt;
       
   149             }
       
   150             while (toupper(*searchIt++) == toupper(*matchIt++));
       
   151 
       
   152             // No match
       
   153             it = restart;
       
   154         }
       
   155     }
       
   156 
       
   157     return -1;
       
   158 }
       
   159 
       
   160 static bool insensitiveEqual(const QByteArray& lhs, const QByteArray& rhs)
       
   161 {
       
   162     if (lhs.isNull() || rhs.isNull())
       
   163         return (lhs.isNull() && rhs.isNull());
       
   164 
       
   165     if (lhs.length() != rhs.length())
       
   166         return false;
       
   167 
       
   168     return insensitiveIndexOf(lhs, rhs) == 0;
       
   169 }
       
   170 
       
   171 static QByteArray charsetForInput(const QString& input)
       
   172 {
       
   173     // See if this input needs encoding
       
   174     bool latin1 = false;
       
   175 
       
   176     const QChar* it = input.constData();
       
   177     const QChar* const end = it + input.length();
       
   178     for ( ; it != end; ++it)
       
   179     {
       
   180         if ((*it).unicode() > 0xff)
       
   181         {
       
   182             // Multi-byte characters included - we need to use UTF-8
       
   183             return QByteArray("UTF-8");
       
   184         }
       
   185         else if (!latin1 && ((*it).unicode() > 0x7f))
       
   186         {
       
   187             // We need encoding from latin-1
       
   188             latin1 = true;
       
   189         }
       
   190     }
       
   191 
       
   192     return (latin1? QByteArray("ISO-8859-1") : QByteArray());
       
   193 }
       
   194 
       
   195 static QTextCodec* codecForName(const QByteArray& charset, bool translateAscii = true)
       
   196 {
       
   197     QByteArray encoding(charset.toLower());
       
   198 
       
   199     if (!encoding.isEmpty())
       
   200     {
       
   201         int index;
       
   202 
       
   203         if (translateAscii && encoding.contains("ascii")) 
       
   204         {
       
   205             // We'll assume the text is plain ASCII, to be extracted to Latin-1
       
   206             encoding = "ISO-8859-1";
       
   207         }
       
   208         else if ((index = encoding.indexOf('*')) != -1)
       
   209         {
       
   210             // This charset specification includes a trailing language specifier
       
   211             encoding = encoding.left(index);
       
   212         }
       
   213 
       
   214         return QTextCodec::codecForName(encoding);
       
   215     }
       
   216 
       
   217     return 0;
       
   218 }
       
   219 
       
   220 static QByteArray fromUnicode(const QString& input, const QByteArray& charset)
       
   221 {
       
   222     if (!charset.isEmpty() && (insensitiveIndexOf("ascii", charset) == -1))
       
   223     {
       
   224         // See if we can convert using the nominated charset
       
   225         if (QTextCodec* textCodec = codecForName(charset))
       
   226             return textCodec->fromUnicode(input);
       
   227 
       
   228         qWarning() << "fromUnicode: unable to find codec for charset:" << charset;
       
   229     }
       
   230 
       
   231     return to7BitAscii(input.toLatin1());
       
   232 }
       
   233 
       
   234 static QString toUnicode(const QByteArray& input, const QByteArray& charset)
       
   235 {
       
   236     if (!charset.isEmpty() && (insensitiveIndexOf("ascii", charset) == -1))
       
   237     {
       
   238         // See if we can convert using the nominated charset
       
   239         if (QTextCodec* textCodec = codecForName(charset))
       
   240             return textCodec->toUnicode(input);
       
   241 
       
   242         qWarning() << "toUnicode: unable to find codec for charset:" << charset;
       
   243     }
       
   244 
       
   245     return to7BitAscii(QString::fromLatin1(input.constData(), input.length()));
       
   246 }
       
   247 
       
   248 static QMailMessageBody::TransferEncoding encodingForName(const QByteArray& name)
       
   249 {
       
   250     QByteArray ciName = name.toLower();
       
   251 
       
   252     if (ciName == "7bit")
       
   253         return QMailMessageBody::SevenBit;
       
   254     if (ciName == "8bit")
       
   255         return QMailMessageBody::EightBit;
       
   256     if (ciName == "base64")
       
   257         return QMailMessageBody::Base64;
       
   258     if (ciName == "quoted-printable")
       
   259         return QMailMessageBody::QuotedPrintable;
       
   260     if (ciName == "binary")
       
   261         return QMailMessageBody::Binary;
       
   262 
       
   263     return QMailMessageBody::NoEncoding;
       
   264 }
       
   265 
       
   266 static const char* nameForEncoding(QMailMessageBody::TransferEncoding te)
       
   267 {
       
   268     switch( te ) 
       
   269     {
       
   270         case QMailMessageBody::SevenBit:
       
   271             return "7bit";
       
   272         case QMailMessageBody::EightBit:
       
   273             return "8bit";
       
   274         case QMailMessageBody::QuotedPrintable:
       
   275             return "quoted-printable";
       
   276         case QMailMessageBody::Base64:
       
   277             return "base64";
       
   278         case QMailMessageBody::Binary:
       
   279             return "binary";
       
   280         case QMailMessageBody::NoEncoding:
       
   281             break;
       
   282     }
       
   283 
       
   284     return 0;
       
   285 }
       
   286 
       
   287 static QMailCodec* codecForEncoding(QMailMessageBody::TransferEncoding te, bool textualData)
       
   288 {
       
   289     switch( te ) 
       
   290     {
       
   291         case QMailMessageBody::NoEncoding:
       
   292         case QMailMessageBody::Binary:
       
   293             return new QMailPassThroughCodec();
       
   294 
       
   295         case QMailMessageBody::SevenBit:
       
   296         case QMailMessageBody::EightBit:
       
   297             return (textualData ? static_cast<QMailCodec*>(new QMailLineEndingCodec()) : new QMailPassThroughCodec());
       
   298 
       
   299         case QMailMessageBody::QuotedPrintable:
       
   300             return new QMailQuotedPrintableCodec(textualData ? QMailQuotedPrintableCodec::Text : QMailQuotedPrintableCodec::Binary, QMailQuotedPrintableCodec::Rfc2045);
       
   301 
       
   302         case QMailMessageBody::Base64:
       
   303             return new QMailBase64Codec(textualData ? QMailBase64Codec::Text : QMailBase64Codec::Binary);
       
   304     }
       
   305 
       
   306     return 0;
       
   307 }
       
   308 
       
   309 static QMailCodec* codecForEncoding(QMailMessageBody::TransferEncoding te, const QMailMessageContentType& content)
       
   310 {
       
   311     return codecForEncoding(te, insensitiveEqual(content.type(), "text"));
       
   312 }
       
   313 
       
   314 //  Needs an encoded word of the form =?charset?q?word?=
       
   315 static QString decodeWord(const QByteArray& encodedWord)
       
   316 {
       
   317     QString result;
       
   318     int index[4];
       
   319 
       
   320     // Find the parts of the input
       
   321     index[0] = encodedWord.indexOf("=?");
       
   322     if (index[0] != -1)
       
   323     {
       
   324         index[1] = encodedWord.indexOf('?', index[0] + 2);
       
   325         if (index[1] != -1)
       
   326         {
       
   327             index[2] = encodedWord.indexOf('?', index[1] + 1);
       
   328             index[3] = encodedWord.lastIndexOf("?=");
       
   329             if ((index[2] != -1) && (index[3] > index[2]))
       
   330             {
       
   331                 QByteArray charset = QMail::unquoteString(encodedWord.mid(index[0] + 2, (index[1] - index[0] - 2)));
       
   332                 QByteArray encoding = encodedWord.mid(index[1] + 1, (index[2] - index[1] - 1)).toUpper();
       
   333                 QByteArray encoded = encodedWord.mid(index[2] + 1, (index[3] - index[2] - 1));
       
   334 
       
   335                 if (encoding == "Q")
       
   336                 {
       
   337                     QMailQuotedPrintableCodec codec(QMailQuotedPrintableCodec::Text, QMailQuotedPrintableCodec::Rfc2047);
       
   338                     result = codec.decode(encoded, charset);
       
   339                 }
       
   340                 else if (encoding == "B")
       
   341                 {
       
   342                     QMailBase64Codec codec(QMailBase64Codec::Binary);
       
   343                     result = codec.decode(encoded, charset);
       
   344                 }
       
   345             }
       
   346         }
       
   347     }
       
   348 
       
   349     if (result.isEmpty())
       
   350         result = encodedWord;
       
   351 
       
   352     return result;
       
   353 }
       
   354 
       
   355 static QByteArray generateEncodedWord(const QByteArray& codec, char encoding, const QByteArray& text)
       
   356 {
       
   357     QByteArray result("=?");
       
   358     result.append(codec);
       
   359     result.append('?');
       
   360     result.append(encoding);
       
   361     result.append('?');
       
   362     result.append(text);
       
   363     result.append("?=");
       
   364     return result;
       
   365 }
       
   366 
       
   367 static QByteArray generateEncodedWord(const QByteArray& codec, char encoding, const QList<QByteArray>& list)
       
   368 {
       
   369     QByteArray result;
       
   370 
       
   371     foreach (const QByteArray& item, list)
       
   372     {
       
   373         if (!result.isEmpty())
       
   374             result.append(' ');
       
   375 
       
   376         result.append(generateEncodedWord(codec, encoding, item));
       
   377     }
       
   378 
       
   379     return result;
       
   380 }
       
   381 
       
   382 static QList<QByteArray> split(const QByteArray& input, const QByteArray& separator)
       
   383 {
       
   384     QList<QByteArray> result;
       
   385 
       
   386     int index = -1;
       
   387     int lastIndex = -1;
       
   388     do
       
   389     {
       
   390         lastIndex = index;
       
   391         index = input.indexOf(separator, lastIndex + 1);
       
   392 
       
   393         int offset = (lastIndex == -1 ? 0 : lastIndex + separator.length());
       
   394         int length = (index == -1 ? -1 : index - offset);
       
   395         result.append(input.mid(offset, length));
       
   396     } while (index != -1);
       
   397 
       
   398     return result;
       
   399 }
       
   400 
       
   401 static QByteArray encodeWord(const QString &text, const QByteArray& cs, bool* encoded)
       
   402 {
       
   403     // Do we need to encode this input?
       
   404     QByteArray charset(cs);
       
   405     if (charset.isEmpty())
       
   406         charset = charsetForInput(text);
       
   407 
       
   408     if (encoded)
       
   409         *encoded = true;
       
   410 
       
   411     // We can't allow more than 75 chars per encoded-word, including the boiler plate...
       
   412     int maximumEncoded = 75 - 7 - charset.length();
       
   413 
       
   414     // If this is an encodedWord, we need to include any whitespace that we don't want to lose
       
   415     if (insensitiveIndexOf("utf-8", charset) == 0)
       
   416     {
       
   417         QMailBase64Codec codec(QMailBase64Codec::Binary, maximumEncoded);
       
   418         QByteArray encoded = codec.encode(text, charset);
       
   419         return generateEncodedWord(charset, 'B', split(encoded, QMailMessage::CRLF));
       
   420     }
       
   421     else if (insensitiveIndexOf("iso-8859-", charset) == 0)
       
   422     {
       
   423         QMailQuotedPrintableCodec codec(QMailQuotedPrintableCodec::Text, QMailQuotedPrintableCodec::Rfc2047, maximumEncoded);
       
   424         QByteArray encoded = codec.encode(text, charset);
       
   425         return generateEncodedWord(charset, 'Q', split(encoded, "=\n"));
       
   426     }
       
   427 
       
   428     if (encoded)
       
   429         *encoded = false;
       
   430 
       
   431     return to7BitAscii(text);
       
   432 }
       
   433 
       
   434 static QString decodeWordSequence(const QByteArray& str)
       
   435 {
       
   436     static const QRegExp whitespace("^\\s+$");
       
   437 
       
   438     QString out;
       
   439 
       
   440     // Any idea why this isn't matching?
       
   441     //QRegExp encodedWord("\\b=\\?\\S+\\?\\S+\\?\\S*\\?=\\b");
       
   442     QRegExp encodedWord("=\\?\\S+\\?\\S+\\?\\S*\\?=");
       
   443 
       
   444     int pos = 0;
       
   445     int lastPos = 0;
       
   446     int length = str.length();
       
   447 
       
   448     while (pos != -1) {
       
   449         pos = encodedWord.indexIn(str, pos);
       
   450         if (pos != -1) {
       
   451             int endPos = pos + encodedWord.matchedLength();
       
   452 
       
   453             if ( ((pos == 0) || (::isspace(str[pos - 1]))) &&
       
   454                  ((endPos == length) || (::isspace(str[endPos]))) ) {
       
   455 
       
   456                 QString preceding(str.mid(lastPos, (pos - lastPos)));
       
   457                 QString decoded = decodeWord(str.mid(pos, (endPos - pos)));
       
   458 
       
   459                 // If there is only whitespace between two encoded words, it should not be included
       
   460                 if (!whitespace.exactMatch(preceding))
       
   461                     out.append(preceding);
       
   462 
       
   463                 out.append(decoded);
       
   464 
       
   465                 pos = endPos;
       
   466                 lastPos = pos;
       
   467             }
       
   468             else
       
   469                 pos = endPos;
       
   470         }
       
   471     }
       
   472 
       
   473     // Copy anything left
       
   474     out.append(str.mid(lastPos));
       
   475 
       
   476     return out;
       
   477 }
       
   478 
       
   479 enum EncodingTokenType
       
   480 {
       
   481     Whitespace,
       
   482     Word,
       
   483     Quote
       
   484 };
       
   485 
       
   486 typedef QPair<const QChar*, int> TokenRange;
       
   487 typedef QPair<EncodingTokenType, TokenRange> Token;
       
   488 
       
   489 static Token makeToken(EncodingTokenType type, const QChar* begin, const QChar* end, bool escaped)
       
   490 {
       
   491     return qMakePair(type, qMakePair(begin, (int)(end - begin) - (escaped ? 1 : 0)));
       
   492 }
       
   493 
       
   494 static QList<Token> tokenSequence(const QString& input)
       
   495 {
       
   496     QList<Token> result;
       
   497 
       
   498     bool escaped = false;
       
   499 
       
   500     const QChar* it = input.constData();
       
   501     const QChar* const end = it + input.length();
       
   502     if (it != end) 
       
   503     {
       
   504         const QChar* token = it;
       
   505         EncodingTokenType state = ((*it) == '"' ? Quote : ((*it).isSpace() ? Whitespace : Word)); 
       
   506 
       
   507         for (++it; it != end; ++it) 
       
   508         {
       
   509             if (!escaped && (*it == '\\')) 
       
   510             {
       
   511                 escaped = true;
       
   512                 continue;
       
   513             }
       
   514 
       
   515             if (state == Quote)
       
   516             {
       
   517                 // This quotation mark is a token by itself
       
   518                 result.append(makeToken(state, token, it, escaped));
       
   519 
       
   520                 state = ((*it) == '"' && !escaped ? Quote : ((*it).isSpace() ? Whitespace : Word)); 
       
   521                 token = it;
       
   522             }
       
   523             else if (state == Whitespace)
       
   524             {
       
   525                 if (!(*it).isSpace())
       
   526                 {
       
   527                     // We have passed the end of this whitespace-sequence
       
   528                     result.append(makeToken(state, token, it, escaped));
       
   529 
       
   530                     state = ((*it) == '"' && !escaped ? Quote : Word);
       
   531                     token = it;
       
   532                 }
       
   533             }
       
   534             else
       
   535             {
       
   536                 if ((*it).isSpace() || ((*it) == '"' && !escaped))
       
   537                 {
       
   538                     // We have passed the end of this word
       
   539                     result.append(makeToken(state, token, it, escaped));
       
   540 
       
   541                     state = ((*it).isSpace() ? Whitespace : Quote);
       
   542                     token = it;
       
   543                 }
       
   544             }
       
   545 
       
   546             escaped = false;
       
   547         }
       
   548 
       
   549         result.append(makeToken(state, token, it, false));
       
   550     }
       
   551 
       
   552     return result;
       
   553 }
       
   554 
       
   555 static QByteArray encodeWordSequence(const QString& str, const QByteArray& charset)
       
   556 {
       
   557     QByteArray result;
       
   558 
       
   559     bool quoted = false;
       
   560     bool tokenEncoded = false;
       
   561     QString quotedText;
       
   562     QString heldWhitespace;
       
   563 
       
   564     foreach (const Token& token, tokenSequence(str))
       
   565     {
       
   566         QString chars = QString::fromRawData(token.second.first, token.second.second);
       
   567 
       
   568         // See if we're processing some quoted words
       
   569         if (quoted)
       
   570         {
       
   571             if (token.first == Quote)
       
   572             {
       
   573                 // We have reached the end of a quote sequence
       
   574                 quotedText.append(chars);
       
   575 
       
   576                 bool lastEncoded = tokenEncoded;
       
   577 
       
   578                 QByteArray output = encodeWord(heldWhitespace + quotedText, charset, &tokenEncoded);
       
   579 
       
   580                 quotedText = QString();
       
   581                 quoted = false;
       
   582                 heldWhitespace = QString();
       
   583 
       
   584                 if (lastEncoded && tokenEncoded)
       
   585                     result.append(' ');
       
   586                 result.append(output);
       
   587             }
       
   588             else
       
   589             {
       
   590                 quotedText.append(chars);
       
   591             }
       
   592         }
       
   593         else
       
   594         {
       
   595             if (token.first == Quote)
       
   596             {
       
   597                 // This token begins a quoted sequence
       
   598                 quotedText = chars;
       
   599                 quoted = true;
       
   600             }
       
   601             else
       
   602             {
       
   603                 if (token.first == Word)
       
   604                 {
       
   605                     bool lastEncoded = tokenEncoded;
       
   606 
       
   607                     // See if this token needs encoding
       
   608                     QByteArray output = encodeWord(heldWhitespace + chars, charset, &tokenEncoded);
       
   609                     heldWhitespace = QString();
       
   610 
       
   611                     if (lastEncoded && tokenEncoded)
       
   612                         result.append(' ');
       
   613                     result.append(output);
       
   614                 }
       
   615                 else // whitespace
       
   616                 {
       
   617                     // If the last token was an encoded-word, we may need to include this
       
   618                     // whitespace into the next token
       
   619                     if (tokenEncoded)
       
   620                         heldWhitespace.append(chars);
       
   621                     else
       
   622                         result.append(chars.toAscii());
       
   623                 }
       
   624             }
       
   625         }
       
   626     }
       
   627 
       
   628     return result;
       
   629 }
       
   630 
       
   631 static int hexValue(char value)
       
   632 {
       
   633     // Although RFC 2231 requires capitals, we may as well accept miniscules too
       
   634     if (value >= 'a')
       
   635         return (((value - 'a') + 10) & 0x0f);
       
   636     if (value >= 'A')
       
   637         return (((value - 'A') + 10) & 0x0f);
       
   638 
       
   639     return ((value - '0') & 0x0f);
       
   640 }
       
   641 
       
   642 static int hexValue(const char* it)
       
   643 {
       
   644     return ((hexValue(*it) << 4) | hexValue(*(it + 1)));
       
   645 }
       
   646 
       
   647 static QString decodeParameterText(const QByteArray& text, const QByteArray& charset)
       
   648 {
       
   649     QByteArray decoded;
       
   650     decoded.reserve(text.length());
       
   651 
       
   652     // Decode any encoded bytes in the data
       
   653     const char* it = text.constData();
       
   654     for (const char* const end = it + text.length(); it != end; ++it)
       
   655     {
       
   656         if (*it == '%')
       
   657         {
       
   658             if ((end - it) > 2)
       
   659                 decoded.append(hexValue(it + 1));
       
   660 
       
   661             it += 2;
       
   662         }
       
   663         else
       
   664             decoded.append(*it);
       
   665     }
       
   666 
       
   667     // Decoded contains a bytestream - decode to unicode text if possible
       
   668     return toUnicode(decoded, charset);
       
   669 }
       
   670 
       
   671 //  Needs an encoded parameter of the form charset'language'text
       
   672 static QString decodeParameter(const QByteArray& encodedParameter)
       
   673 {
       
   674     QRegExp parameterFormat("([^']*)'(?:[^']*)'(.*)");
       
   675     if (parameterFormat.exactMatch(encodedParameter))
       
   676         return decodeParameterText(parameterFormat.cap(2).toLatin1(), parameterFormat.cap(1).toLatin1());
       
   677 
       
   678     // Treat the whole thing as input, and deafult the charset to ascii
       
   679     // This is not required by the RFC, since the input is illegal.  But, it 
       
   680     // seems ok since the parameter name has already indicated that the text 
       
   681     // should be encoded...
       
   682     return decodeParameterText(encodedParameter, "us-ascii");
       
   683 }
       
   684 
       
   685 static char hexRepresentation(int value)
       
   686 {
       
   687     value &= 0x0f;
       
   688 
       
   689     if (value < 10)
       
   690         return ('0' + value);
       
   691     return ('A' + (value - 10));
       
   692 }
       
   693 
       
   694 static QByteArray generateEncodedParameter(const QByteArray& charset, const QByteArray& language, const QByteArray& text)
       
   695 {
       
   696     QByteArray result(charset);
       
   697     QByteArray lang(language);
       
   698 
       
   699     // If the charset contains a language part, extract it
       
   700     int index = result.indexOf('*');
       
   701     if (index != -1)
       
   702     {
       
   703         // If no language is specfied, use the extracted part
       
   704         if (lang.isEmpty())
       
   705             lang = result.mid(index + 1);
       
   706 
       
   707         result = result.left(index);
       
   708     }
       
   709 
       
   710     result.append('\'');
       
   711     result.append(lang);
       
   712     result.append('\'');
       
   713     
       
   714     // Have a guess at how long the result will be
       
   715     result.reserve(result.length() + (2 * text.length()));
       
   716 
       
   717     // We could encode the exact set of permissible characters here, but they're basically the alphanumerics
       
   718     const char* it = text.constData();
       
   719     const char* const end = it + text.length();
       
   720     for ( ; it != end; ++it) {
       
   721         if (::isalnum(static_cast<unsigned char>(*it))) {
       
   722             result.append(*it);
       
   723         } else {
       
   724             // Encode to hex
       
   725             int value = (*it);
       
   726             result.append('%').append(hexRepresentation(value >> 4)).append(hexRepresentation(value));
       
   727         }
       
   728     }
       
   729 
       
   730     return result;
       
   731 }
       
   732 
       
   733 static QByteArray encodeParameter(const QString &text, const QByteArray& charset, const QByteArray& language)
       
   734 {
       
   735     QByteArray encoding(charset);
       
   736     if (encoding.isEmpty())
       
   737         encoding = charsetForInput(text);
       
   738 
       
   739     return generateEncodedParameter(encoding, language, fromUnicode(text, encoding));
       
   740 }
       
   741 
       
   742 static QByteArray removeComments(const QByteArray& input, int (*classifier)(int), bool acceptedResult = true)
       
   743 {
       
   744     QByteArray result;
       
   745 
       
   746     int commentDepth = 0;
       
   747     bool quoted = false;
       
   748     bool escaped = false;
       
   749 
       
   750     const char* it = input.constData();
       
   751     const char* const end = it + input.length();
       
   752     for ( ; it != end; ++it ) {
       
   753         if ( !escaped && ( *it == '\\' ) ) {
       
   754             escaped = true;
       
   755             continue;
       
   756         }
       
   757 
       
   758         if ( *it == '(' && !escaped && !quoted ) {
       
   759             commentDepth += 1;
       
   760         }
       
   761         else if ( *it == ')' && !escaped && !quoted && ( commentDepth > 0 ) ) {
       
   762             commentDepth -= 1;
       
   763         }
       
   764         else {
       
   765             bool quoteProcessed = false;
       
   766             if ( !quoted && *it == '"' && !escaped ) {
       
   767                 quoted = true;
       
   768                 quoteProcessed = true;
       
   769             }
       
   770 
       
   771             if ( commentDepth == 0 ) {
       
   772                 if ( quoted || (bool((*classifier)(*it)) == acceptedResult) )
       
   773                     result.append( *it );
       
   774             }
       
   775 
       
   776             if ( quoted && !quoteProcessed && *it == '"' && !escaped ) {
       
   777                 quoted = false;
       
   778             }
       
   779         }
       
   780 
       
   781         escaped = false;
       
   782     }
       
   783 
       
   784     return result;
       
   785 }
       
   786 
       
   787 
       
   788 // Necessary when writing to QDataStream, because the string/char literal is encoded
       
   789 // in various pre-processed ways...
       
   790 
       
   791 struct DataString
       
   792 {
       
   793     DataString(char datum) : _datum(datum), _data(0), _length(0) {};
       
   794     DataString(const char* data) : _datum('\0'), _data(data), _length(strlen(_data)) {};
       
   795     DataString(const QByteArray& array) : _datum('\0'), _data(array.constData()), _length(array.length()) {};
       
   796 
       
   797     inline QDataStream& toDataStream(QDataStream& out) const
       
   798     {
       
   799         if (_data)
       
   800             out.writeRawData(_data, _length);
       
   801         else if (_datum == '\n')
       
   802             // Ensure that line-feeds are always CRLF sequences
       
   803             out.writeRawData(QMailMessage::CRLF, 2);
       
   804         else if (_datum != '\0')
       
   805             out.writeRawData(&_datum, 1);
       
   806 
       
   807         return out;
       
   808     }
       
   809 
       
   810 private:
       
   811     char _datum;
       
   812     const char* _data;
       
   813     int _length;
       
   814 };
       
   815 
       
   816 QDataStream& operator<<(QDataStream& out, const DataString& dataString)
       
   817 {
       
   818     return dataString.toDataStream(out);
       
   819 }
       
   820 
       
   821 
       
   822 /* QMailMessageHeaderField */
       
   823 
       
   824 QMailMessageHeaderFieldPrivate::QMailMessageHeaderFieldPrivate()
       
   825     : QPrivateImplementationBase(this),
       
   826       _structured(true)
       
   827 {
       
   828 }
       
   829 
       
   830 QMailMessageHeaderFieldPrivate::QMailMessageHeaderFieldPrivate(const QByteArray& text, bool structured)
       
   831     : QPrivateImplementationBase(this)
       
   832 {
       
   833     parse(text, structured);
       
   834 }
       
   835 
       
   836 QMailMessageHeaderFieldPrivate::QMailMessageHeaderFieldPrivate(const QByteArray& id, const QByteArray& text, bool structured)
       
   837     : QPrivateImplementationBase(this)
       
   838 {
       
   839     _id = id;
       
   840     parse(text, structured);
       
   841 }
       
   842 
       
   843 static bool validExtension(const QByteArray& trailer, int* number = 0, bool* encoded = 0)
       
   844 {
       
   845     // Extensions according to RFC 2231:
       
   846     QRegExp extensionFormat("(?:\\*(\\d+))?(\\*?)");
       
   847     if (extensionFormat.exactMatch(trailer))
       
   848     {
       
   849         if (number)
       
   850             *number = extensionFormat.cap(1).toInt();
       
   851         if (encoded)
       
   852             *encoded = !extensionFormat.cap(2).isEmpty();
       
   853 
       
   854         return true;
       
   855     }
       
   856     else
       
   857         return false;
       
   858 }
       
   859 
       
   860 static bool matchingParameter(const QByteArray& name, const QByteArray& other, bool* encoded = 0)
       
   861 {
       
   862     QByteArray match(name.trimmed());
       
   863 
       
   864     int index = insensitiveIndexOf(match, other);
       
   865     if (index == -1)
       
   866         return false;
       
   867 
       
   868     if (index > 0)
       
   869     {
       
   870         // Ensure that every preceding character is whitespace
       
   871         QByteArray leader(other.left(index).trimmed());
       
   872         if (!leader.isEmpty())
       
   873             return false;
       
   874     }
       
   875 
       
   876     int lastIndex = index + match.length() - 1;
       
   877     index = other.indexOf('=', lastIndex);
       
   878     if (index == -1)
       
   879         index = other.length();
       
   880 
       
   881     // Ensure that there is only whitespace between the matched name and the end of the name
       
   882     if ((index - lastIndex) > 1)
       
   883     {
       
   884         QByteArray trailer(other.mid(lastIndex + 1, (index - lastIndex)).trimmed());
       
   885         if (!trailer.isEmpty())
       
   886             return validExtension(trailer, 0, encoded);
       
   887     }
       
   888 
       
   889     return true;
       
   890 }
       
   891 
       
   892 void QMailMessageHeaderFieldPrivate::addParameter(const QByteArray& name, const QByteArray& value)
       
   893 {
       
   894     _parameters.append(qMakePair(name, QMail::unquoteString(value)));
       
   895 }
       
   896 
       
   897 void QMailMessageHeaderFieldPrivate::parse(const QByteArray& text, bool structured)
       
   898 {
       
   899     _structured = structured;
       
   900 
       
   901     // Parse text into main and params
       
   902     const char* const begin = text.constData();
       
   903     const char* const end = begin + text.length();
       
   904 
       
   905     bool malformed = false;
       
   906 
       
   907     const char* token = begin;
       
   908     const char* firstToken = begin;
       
   909     const char* it = begin;
       
   910     const char* separator = 0;
       
   911     for (bool quoted = false; it != end; ++it)
       
   912     {
       
   913         if (*it == '"') {
       
   914             quoted = !quoted;
       
   915         }
       
   916         else if (*it == ':' && !quoted && token == begin) {
       
   917             // This is the end of the field id
       
   918             if (_id.isEmpty()) {
       
   919                 _id = QByteArray(token, (it - token)).trimmed();
       
   920                 token = (it + 1);
       
   921             }
       
   922             else if (_structured) {
       
   923                 // If this is a structured header, there can be only one colon
       
   924                 token = (it + 1);
       
   925             }
       
   926             firstToken = token;
       
   927         }
       
   928         else if (*it == '=' && !quoted && structured) {
       
   929             if (separator == 0) {
       
   930                 // This is a parameter separator
       
   931                 separator = it;
       
   932             }
       
   933             else  {
       
   934                 // It would be nice to identify extra '=' chars, but it's too hard
       
   935                 // to separate them from encoded-word formations...
       
   936                 //malformed = true;
       
   937             }
       
   938         }
       
   939         else if (*it == ';' && !quoted && structured) {
       
   940             // This is the end of a token
       
   941             if (_content.isEmpty()) {
       
   942                 _content = QByteArray(token, (it - token)).trimmed();
       
   943             }
       
   944             else if ((separator > token) && ((separator + 1) < it)) {
       
   945                 QByteArray name = QByteArray(token, (separator - token)).trimmed();
       
   946                 QByteArray value = QByteArray(separator + 1, (it - separator - 1)).trimmed();
       
   947 
       
   948                 if (!name.isEmpty() && !value.isEmpty())
       
   949                     addParameter(name, value);
       
   950             }
       
   951             else {
       
   952                 malformed = true;
       
   953             }
       
   954 
       
   955             token = (it + 1);
       
   956             separator = 0;
       
   957         }
       
   958     }
       
   959 
       
   960     if (token != end) {
       
   961         if (_id.isEmpty()) {
       
   962             _id = QByteArray(token, (end - token)).trimmed();
       
   963         }
       
   964         else if (_content.isEmpty()) {
       
   965             _content = QByteArray(token, (end - token)).trimmed();
       
   966         }
       
   967         else if ((separator > token) && ((separator + 1) < end) && !malformed) {
       
   968             QByteArray name = QByteArray(token, (separator - token)).trimmed();
       
   969             QByteArray value = QByteArray(separator + 1, (end - separator - 1)).trimmed();
       
   970 
       
   971             if (!name.isEmpty() && !value.isEmpty())
       
   972                 addParameter(name, value);
       
   973         }
       
   974         else if (_structured) {
       
   975             malformed = true;
       
   976         }
       
   977     }
       
   978 }
       
   979 
       
   980 bool QMailMessageHeaderFieldPrivate::operator== (const QMailMessageHeaderFieldPrivate& other) const
       
   981 {
       
   982     if (!insensitiveEqual(_id, other._id))
       
   983         return false;
       
   984     
       
   985     if (_content != other._content)
       
   986         return false;
       
   987 
       
   988     if (_parameters.count() != other._parameters.count())
       
   989         return false;
       
   990 
       
   991     QList<QMailMessageHeaderField::ParameterType>::const_iterator it = _parameters.begin(), end = _parameters.end();
       
   992     QList<QMailMessageHeaderField::ParameterType>::const_iterator oit = other._parameters.begin();
       
   993     for ( ; it != end; ++it, ++oit)
       
   994         if (((*it).first != (*oit).first) || ((*it).second != (*oit).second))
       
   995             return false;
       
   996 
       
   997     return true;
       
   998 }
       
   999 
       
  1000 bool QMailMessageHeaderFieldPrivate::isNull() const
       
  1001 {
       
  1002     return (_id.isNull() && _content.isNull());
       
  1003 }
       
  1004 
       
  1005 QByteArray QMailMessageHeaderFieldPrivate::id() const
       
  1006 {
       
  1007     return _id;
       
  1008 }
       
  1009 
       
  1010 void QMailMessageHeaderFieldPrivate::setId(const QByteArray& text)
       
  1011 {
       
  1012     _id = text;
       
  1013 }
       
  1014 
       
  1015 QByteArray QMailMessageHeaderFieldPrivate::content() const
       
  1016 {
       
  1017     return _content;
       
  1018 }
       
  1019 
       
  1020 void QMailMessageHeaderFieldPrivate::setContent(const QByteArray& text)
       
  1021 {
       
  1022     _content = text;
       
  1023 }
       
  1024 
       
  1025 QByteArray QMailMessageHeaderFieldPrivate::parameter(const QByteArray& name) const
       
  1026 {
       
  1027     // Coalesce folded parameters into a single return value
       
  1028     QByteArray result;
       
  1029 
       
  1030     QByteArray param = name.trimmed();
       
  1031     foreach (const QMailMessageContentType::ParameterType& parameter, _parameters) {
       
  1032         if (matchingParameter(param, parameter.first))
       
  1033             result.append(parameter.second);
       
  1034     }
       
  1035 
       
  1036     return result;
       
  1037 }
       
  1038 
       
  1039 void QMailMessageHeaderFieldPrivate::setParameter(const QByteArray& name, const QByteArray& value)
       
  1040 {
       
  1041     if (!_structured)
       
  1042         return;
       
  1043 
       
  1044     QByteArray param = name.trimmed();
       
  1045 
       
  1046     bool encoded = false;
       
  1047     int index = param.indexOf('*');
       
  1048     if (index != -1) {
       
  1049         encoded = true;
       
  1050         param = param.left(index);
       
  1051     }
       
  1052 
       
  1053     // Find all existing parts of this parameter, if present
       
  1054     QList<QList<QMailMessageHeaderField::ParameterType>::iterator> matches;
       
  1055     QList<QMailMessageHeaderField::ParameterType>::iterator it = _parameters.begin(), end = _parameters.end();
       
  1056     for ( ; it != end; ++it) {
       
  1057         if (matchingParameter(param, (*it).first))
       
  1058             matches.prepend(it);
       
  1059     }
       
  1060 
       
  1061     while (matches.count() > 1)
       
  1062         _parameters.erase(matches.takeFirst());
       
  1063     if (matches.count() == 1)
       
  1064         it = matches.takeFirst();
       
  1065     
       
  1066     // If the value is too long to fit on one line, break it into manageable pieces
       
  1067     const int maxInputLength = 78 - 9 - param.length();
       
  1068 
       
  1069     if (value.length() > maxInputLength) {
       
  1070         // We have multiple pieces to insert
       
  1071         QList<QByteArray> pieces;
       
  1072         QByteArray input(value);
       
  1073         do
       
  1074         {
       
  1075             pieces.append(input.left(maxInputLength));
       
  1076             input = input.mid(maxInputLength);
       
  1077         } while (input.length());
       
  1078 
       
  1079         if (it == end) {
       
  1080             // Append each piece at the end
       
  1081             int n = 0;
       
  1082             while (pieces.count() > 0) {
       
  1083                 QByteArray id(param);
       
  1084                 id.append('*').append(QByteArray::number(n));
       
  1085                 if (encoded && (n == 0))
       
  1086                     id.append('*');
       
  1087 
       
  1088                 _parameters.append(qMakePair(id, pieces.takeFirst()));
       
  1089                 ++n;
       
  1090             }
       
  1091         }
       
  1092         else {
       
  1093             // Overwrite the remaining instance of the parameter, and place any 
       
  1094             // following pieces immediately after
       
  1095             int n = pieces.count() - 1;
       
  1096             int initial = n;
       
  1097 
       
  1098             while (pieces.count() > 0) {
       
  1099                 QByteArray id(param);
       
  1100                 id.append('*').append(QByteArray::number(n));
       
  1101                 if (encoded && (n == 0))
       
  1102                     id.append('*');
       
  1103 
       
  1104                 QMailMessageHeaderField::ParameterType parameter = qMakePair(id, pieces.takeLast());
       
  1105                 if (n == initial) {
       
  1106                     // Put the last piece into the existing position
       
  1107                     (*it) = parameter;
       
  1108                 }
       
  1109                 else {
       
  1110                     // Insert before the previous piece, and record the new iterator
       
  1111                     it = _parameters.insert(it, parameter);
       
  1112                 }
       
  1113 
       
  1114                 --n;
       
  1115             }
       
  1116         }
       
  1117     }
       
  1118     else {
       
  1119         // Just one part to insert
       
  1120         QByteArray id(param);
       
  1121         if (encoded)
       
  1122             id.append('*');
       
  1123         QMailMessageHeaderField::ParameterType parameter = qMakePair(id, value);
       
  1124 
       
  1125         if (it == end) {
       
  1126             _parameters.append(parameter);
       
  1127         }
       
  1128         else {
       
  1129             (*it) = parameter;
       
  1130         }
       
  1131     }
       
  1132 }
       
  1133 
       
  1134 bool QMailMessageHeaderFieldPrivate::isParameterEncoded(const QByteArray& name) const
       
  1135 {
       
  1136     QByteArray param = name.trimmed();
       
  1137 
       
  1138     bool encoded = false;
       
  1139     foreach (const QMailMessageContentType::ParameterType& parameter, _parameters)
       
  1140         if (matchingParameter(param, parameter.first, &encoded))
       
  1141             return encoded;
       
  1142 
       
  1143     return false;
       
  1144 }
       
  1145 
       
  1146 void QMailMessageHeaderFieldPrivate::setParameterEncoded(const QByteArray& name)
       
  1147 {
       
  1148     QByteArray param = name.trimmed();
       
  1149 
       
  1150     QList<QMailMessageHeaderField::ParameterType>::iterator it = _parameters.begin(), end = _parameters.end();
       
  1151     for ( ; it != end; ++it) {
       
  1152         bool encoded = false;
       
  1153         if (matchingParameter(param, (*it).first, &encoded)) {
       
  1154             if (!encoded)
       
  1155                 (*it).first.append('*');
       
  1156         }
       
  1157     }
       
  1158 }
       
  1159 
       
  1160 static QByteArray protectedParameter(const QByteArray& value)
       
  1161 {
       
  1162     static const QRegExp whitespace("\\s+");
       
  1163     static const QRegExp tspecials = QRegExp("[<>\\[\\]\\(\\)\\?:;@\\\\,=]");
       
  1164 
       
  1165     if ((whitespace.indexIn(value) != -1) ||
       
  1166         (tspecials.indexIn(value) != -1))
       
  1167         return QMail::quoteString(value);
       
  1168     else
       
  1169         return value;
       
  1170 }
       
  1171 
       
  1172 static bool extendedParameter(const QByteArray& name, QByteArray* truncated = 0, int* number = 0, bool* encoded = 0)
       
  1173 {
       
  1174     QByteArray param(name.trimmed());
       
  1175 
       
  1176     int index = param.indexOf('*');
       
  1177     if (index == -1)
       
  1178         return false;
       
  1179 
       
  1180     if (truncated)
       
  1181         *truncated = param.left(index).trimmed();
       
  1182 
       
  1183     return validExtension(param.mid(index), number, encoded);
       
  1184 }
       
  1185 
       
  1186 QList<QMailMessageHeaderField::ParameterType> QMailMessageHeaderFieldPrivate::parameters() const
       
  1187 {
       
  1188     QList<QMailMessageHeaderField::ParameterType> result;
       
  1189 
       
  1190     foreach (const QMailMessageContentType::ParameterType& param, _parameters) {
       
  1191         QByteArray id;
       
  1192         int number;
       
  1193         if (extendedParameter(param.first, &id, &number)) {
       
  1194             if (number == 0) {
       
  1195                 result.append(qMakePair(id, parameter(id)));
       
  1196             }
       
  1197         }
       
  1198         else {
       
  1199             result.append(param);
       
  1200         }
       
  1201     }
       
  1202 
       
  1203     return result;
       
  1204 }
       
  1205 
       
  1206 QByteArray QMailMessageHeaderFieldPrivate::toString(bool includeName, bool presentable) const
       
  1207 {
       
  1208     if (_id.isEmpty())
       
  1209         return QByteArray();
       
  1210 
       
  1211     QByteArray result;
       
  1212     if (includeName) {
       
  1213         result = _id + ":";
       
  1214     }
       
  1215     
       
  1216     if (!_content.isEmpty()) {
       
  1217         if (includeName)
       
  1218             result += ' ';
       
  1219         result += _content;
       
  1220     }
       
  1221 
       
  1222     if (_structured)
       
  1223     {
       
  1224         foreach (const QMailMessageContentType::ParameterType& parameter, (presentable ? parameters() : _parameters))
       
  1225             result.append("; ").append(parameter.first).append('=').append(protectedParameter(parameter.second));
       
  1226     }
       
  1227 
       
  1228     return result;
       
  1229 }
       
  1230 
       
  1231 static void outputHeaderPart(QDataStream& out, const QByteArray& text, int* lineLength, const int maxLineLength)
       
  1232 {
       
  1233     static const QRegExp whitespace("\\s");
       
  1234 
       
  1235     int remaining = maxLineLength - *lineLength;
       
  1236     if (text.length() <= remaining)
       
  1237     {
       
  1238         out << DataString(text);
       
  1239         *lineLength += text.length();
       
  1240     }
       
  1241     else
       
  1242     {
       
  1243         // See if we can find suitable whitespace to break the line
       
  1244         int wsIndex = -1;
       
  1245         int lastIndex = -1;
       
  1246         int preferredIndex = -1;
       
  1247         do 
       
  1248         {
       
  1249             lastIndex = wsIndex;
       
  1250             if ((lastIndex > 0) && (text[lastIndex - 1] == ';')) {
       
  1251                 // Prefer to split after (possible) parameters
       
  1252                 preferredIndex = lastIndex;
       
  1253             }
       
  1254 
       
  1255             wsIndex = whitespace.indexIn(text, wsIndex + 1);
       
  1256         } while ((wsIndex != -1) && (wsIndex < remaining));
       
  1257 
       
  1258         if (preferredIndex != -1)
       
  1259             lastIndex = preferredIndex;
       
  1260 
       
  1261         if (lastIndex == -1)
       
  1262         {
       
  1263             // We couldn't find any suitable whitespace - just break at the last char
       
  1264             lastIndex = remaining;
       
  1265         }
       
  1266 
       
  1267         if (lastIndex == 0)
       
  1268         {
       
  1269             out << DataString('\n') << DataString(text[0]);
       
  1270             *lineLength = 1;
       
  1271             lastIndex = 1;
       
  1272         }
       
  1273         else
       
  1274         {
       
  1275             out << DataString(text.left(lastIndex)) << DataString('\n');
       
  1276 
       
  1277             if (lastIndex == remaining) {
       
  1278                 // We need to insert some artifical whitespace
       
  1279                 out << DataString('\t');
       
  1280             } else {
       
  1281                 // Append the breaking whitespace (ensure it does not get CRLF-ified)
       
  1282                 out << DataString(QByteArray(1, text[lastIndex]));
       
  1283                 ++lastIndex;
       
  1284             }
       
  1285 
       
  1286             *lineLength = 1;
       
  1287         }
       
  1288 
       
  1289         QByteArray remainder(text.mid(lastIndex));
       
  1290         if (!remainder.isEmpty())
       
  1291             outputHeaderPart(out, remainder, lineLength, maxLineLength);
       
  1292     }
       
  1293 }
       
  1294 
       
  1295 void QMailMessageHeaderFieldPrivate::output(QDataStream& out) const
       
  1296 {
       
  1297     static const int maxLineLength = 78;
       
  1298 
       
  1299     if (_id.isEmpty())
       
  1300         return;
       
  1301 
       
  1302     if (_structured) {
       
  1303         qWarning() << "Unable to output structured header field:" << _id;
       
  1304         return;
       
  1305     }
       
  1306 
       
  1307     QByteArray element(_id);
       
  1308     element.append(':');
       
  1309     out << DataString(element);
       
  1310 
       
  1311     if (!_content.isEmpty()) {
       
  1312         int lineLength = element.length();
       
  1313         outputHeaderPart(out, " " + _content, &lineLength, maxLineLength);
       
  1314     }
       
  1315 
       
  1316     out << DataString('\n');
       
  1317 }
       
  1318 
       
  1319 static bool parameterEncoded(const QByteArray& name)
       
  1320 {
       
  1321     QByteArray param(name.trimmed());
       
  1322     if (param.isEmpty())
       
  1323         return false;
       
  1324 
       
  1325     return (param[param.length() - 1] == '*');
       
  1326 }
       
  1327 
       
  1328 QString QMailMessageHeaderFieldPrivate::decodedContent() const
       
  1329 {
       
  1330     QString result(QMailMessageHeaderField::decodeContent(_content));
       
  1331 
       
  1332     if (_structured)
       
  1333     {
       
  1334         foreach (const QMailMessageContentType::ParameterType& parameter, _parameters) {
       
  1335             QString decoded;
       
  1336             if (parameterEncoded(parameter.first))
       
  1337                 decoded = QMailMessageHeaderField::decodeParameter(protectedParameter(parameter.second));
       
  1338             else
       
  1339                 decoded = protectedParameter(parameter.second);
       
  1340             result.append("; ").append(parameter.first).append('=').append(decoded);
       
  1341         }
       
  1342     }
       
  1343 
       
  1344     return result;
       
  1345 }
       
  1346 
       
  1347 template <typename Stream> 
       
  1348 void QMailMessageHeaderFieldPrivate::serialize(Stream &stream) const
       
  1349 {
       
  1350     stream << _id;
       
  1351     stream << _content;
       
  1352     stream << _structured;
       
  1353     stream << _parameters;
       
  1354 }
       
  1355 
       
  1356 template <typename Stream> 
       
  1357 void QMailMessageHeaderFieldPrivate::deserialize(Stream &stream)
       
  1358 {
       
  1359     stream >> _id;
       
  1360     stream >> _content;
       
  1361     stream >> _structured;
       
  1362     stream >> _parameters;
       
  1363 }
       
  1364 
       
  1365 
       
  1366 /*!
       
  1367     \class QMailMessageHeaderField
       
  1368 
       
  1369     \brief The QMailMessageHeaderField class encapsulates the parsing of message header fields.
       
  1370     
       
  1371     \ingroup messaginglibrary
       
  1372    
       
  1373     QMailMessageHeaderField provides simplified access to the various components of the 
       
  1374     header field, and allows the field content to be extracted in a standardized form.
       
  1375 
       
  1376     The content of a header field may be formed of unstructured text, or it may have an 
       
  1377     internal structure.  If a structured field is specified, QMailMessageHeaderField assumes 
       
  1378     that the contained header field is structured in a format equivalent to that used for the 
       
  1379     RFC 2045 'Content-Type' and RFC 2183 'Content-Disposition' header fields.  If the field 
       
  1380     is unstructured, or conforms to a different structure, then the parameter() and parameters() functions
       
  1381     will return empty results, and the setParameter() function will have no effect.
       
  1382 
       
  1383     QMailMessageHeaderField contains static functions to assist in creating correct
       
  1384     header field content, and presenting header field content.  The encodeWord() and 
       
  1385     decodeWord() functions translate between plain text and the encoded-word specification
       
  1386     defined in RFC 2045.  The encodeParameter() and decodeParameter() functions translate
       
  1387     between plain text and the encoded-parameter format defined in RFC 2231.
       
  1388 
       
  1389     The removeWhitespace() function can be used to remove irrelevant whitespace characters
       
  1390     from a string, and the removeComments() function can remove any comment sequences 
       
  1391     present, encododed according to the RFC 2822 specification.
       
  1392 */
       
  1393 
       
  1394 /*!
       
  1395     \typedef QMailMessageHeaderField::ImplementationType
       
  1396     \internal
       
  1397 */
       
  1398 
       
  1399 /*!
       
  1400     \typedef QMailMessageHeaderField::ParameterType
       
  1401     \internal
       
  1402 */
       
  1403 
       
  1404 /*!
       
  1405     Creates an uninitialised message header field object.
       
  1406 */
       
  1407 QMailMessageHeaderField::QMailMessageHeaderField()
       
  1408     : QPrivatelyImplemented<QMailMessageHeaderFieldPrivate>(new QMailMessageHeaderFieldPrivate())
       
  1409 {
       
  1410 }
       
  1411 
       
  1412 /*!
       
  1413     Creates a message header field object from the data in \a text. If \a fieldType is 
       
  1414     QMailMessageHeaderField::StructuredField, then \a text will be parsed assuming a 
       
  1415     format equivalent to that used for the RFC 2045 'Content-Type' and 
       
  1416     RFC 2183 'Content-Disposition' header fields.
       
  1417 */
       
  1418 QMailMessageHeaderField::QMailMessageHeaderField(const QByteArray& text, FieldType fieldType)
       
  1419     : QPrivatelyImplemented<QMailMessageHeaderFieldPrivate>(new QMailMessageHeaderFieldPrivate(text, (fieldType == StructuredField)))
       
  1420 {
       
  1421 }
       
  1422 
       
  1423 /*!
       
  1424     Creates a message header field object with the field id \a id and the content 
       
  1425     data in \a text.  If \a fieldType is QMailMessageHeaderField::StructuredField, 
       
  1426     then \a text will be parsed assuming a format equivalent to that used for the 
       
  1427     RFC 2045 'Content-Type' and RFC 2183 'Content-Disposition' header fields.
       
  1428 */
       
  1429 QMailMessageHeaderField::QMailMessageHeaderField(const QByteArray& id, const QByteArray& text, FieldType fieldType)
       
  1430     : QPrivatelyImplemented<QMailMessageHeaderFieldPrivate>(new QMailMessageHeaderFieldPrivate(id, text, (fieldType == StructuredField)))
       
  1431 {
       
  1432 }
       
  1433 
       
  1434 /*! \internal */
       
  1435 bool QMailMessageHeaderField::operator== (const QMailMessageHeaderField& other) const
       
  1436 {
       
  1437     return impl(this)->operator==(*other.impl(&other));
       
  1438 }
       
  1439 
       
  1440 /*!
       
  1441     Returns true if the header field has not been initialized.
       
  1442 */
       
  1443 bool QMailMessageHeaderField::isNull() const
       
  1444 {
       
  1445     return impl(this)->isNull();
       
  1446 }
       
  1447 
       
  1448 /*!
       
  1449     Returns the ID of the header field.
       
  1450 */
       
  1451 QByteArray QMailMessageHeaderField::id() const
       
  1452 {
       
  1453     return impl(this)->id();
       
  1454 }
       
  1455 
       
  1456 /*!
       
  1457     Sets the ID of the header field to \a id.
       
  1458 */
       
  1459 void QMailMessageHeaderField::setId(const QByteArray& id)
       
  1460 {
       
  1461     impl(this)->setId(id);
       
  1462 }
       
  1463 
       
  1464 /*!
       
  1465     Returns the content of the header field, without any associated parameters.
       
  1466 */
       
  1467 QByteArray QMailMessageHeaderField::content() const
       
  1468 {
       
  1469     return impl(this)->content();
       
  1470 }
       
  1471 
       
  1472 /*!
       
  1473     Sets the content of the header field to \a text.
       
  1474 */
       
  1475 void QMailMessageHeaderField::setContent(const QByteArray& text)
       
  1476 {
       
  1477     impl(this)->setContent(text);
       
  1478 }
       
  1479 
       
  1480 /*!
       
  1481     Returns the value of the parameter with the name \a name.  
       
  1482     Name comparisons are case-insensitive.
       
  1483 */
       
  1484 QByteArray QMailMessageHeaderField::parameter(const QByteArray& name) const
       
  1485 {
       
  1486     return impl(this)->parameter(name);
       
  1487 }
       
  1488 
       
  1489 /*!
       
  1490     Sets the parameter with the name \a name to have the value \a value, if present; 
       
  1491     otherwise a new parameter is appended with the supplied properties.  If \a name
       
  1492     ends with a single asterisk, the parameter will be flagged as encoded.
       
  1493 
       
  1494     \sa setParameterEncoded()
       
  1495 */
       
  1496 void QMailMessageHeaderField::setParameter(const QByteArray& name, const QByteArray& value)
       
  1497 {
       
  1498     impl(this)->setParameter(name, value);
       
  1499 }
       
  1500 
       
  1501 /*!
       
  1502     Returns true if the parameter with name \a name exists and is marked as encoded 
       
  1503     according to RFC 2231; otherwise returns false.  
       
  1504     Name comparisons are case-insensitive.
       
  1505 */
       
  1506 bool QMailMessageHeaderField::isParameterEncoded(const QByteArray& name) const
       
  1507 {
       
  1508     return impl(this)->isParameterEncoded(name);
       
  1509 }
       
  1510 
       
  1511 /*!
       
  1512     Sets any parameters with the name \a name to be marked as encoded.
       
  1513     Name comparisons are case-insensitive.
       
  1514 */
       
  1515 void QMailMessageHeaderField::setParameterEncoded(const QByteArray& name)
       
  1516 {
       
  1517     impl(this)->setParameterEncoded(name);
       
  1518 }
       
  1519 
       
  1520 /*!
       
  1521     Returns the list of parameters from the header field. For each parameter, the
       
  1522     member \c first contains the name text, and the member \c second contains the value text.
       
  1523 */
       
  1524 QList<QMailMessageHeaderField::ParameterType> QMailMessageHeaderField::parameters() const
       
  1525 {
       
  1526     return impl(this)->parameters();
       
  1527 }
       
  1528 
       
  1529 /*!
       
  1530     Returns the entire header field text as a formatted string, with the name of the field
       
  1531     included if \a includeName is true.  If \a presentable is true, artifacts of RFC 2822 
       
  1532     transmission format such as parameter folding will be removed.  For example: 
       
  1533     
       
  1534     \code
       
  1535     QMailMessageHeaderField hdr;
       
  1536     hdr.setId("Content-Type");
       
  1537     hdr.setContent("text/plain");
       
  1538     hdr.setParameter("charset", "us-ascii");
       
  1539 
       
  1540     QString s = hdr.toString();  // s: "Content-Type: text/plain; charset=us-ascii"
       
  1541     \endcode
       
  1542 */
       
  1543 QByteArray QMailMessageHeaderField::toString(bool includeName, bool presentable) const
       
  1544 {
       
  1545     return impl(this)->toString(includeName, presentable);
       
  1546 }
       
  1547 
       
  1548 /*!
       
  1549     Returns the content of the header field as unicode text.  If the content of the
       
  1550     field contains any encoded-word or encoded-parameter values, they will be decoded on output.
       
  1551 */
       
  1552 QString QMailMessageHeaderField::decodedContent() const
       
  1553 {
       
  1554     return impl(this)->decodedContent();
       
  1555 }
       
  1556 
       
  1557 /*! \internal */
       
  1558 void QMailMessageHeaderField::parse(const QByteArray& text, FieldType fieldType)
       
  1559 {
       
  1560     return impl(this)->parse(text, (fieldType == StructuredField));
       
  1561 }
       
  1562 
       
  1563 /*!
       
  1564     Returns the content of the string \a input encoded into a series of RFC 2045 'encoded-word'
       
  1565     format tokens, each no longer than 75 characters.  The encoding used can be specified in 
       
  1566     \a charset, or can be deduced from the content of \a input if \a charset is empty.
       
  1567 */
       
  1568 QByteArray QMailMessageHeaderField::encodeWord(const QString& input, const QByteArray& charset)
       
  1569 {
       
  1570     return ::encodeWord(input, charset, 0);
       
  1571 }
       
  1572 
       
  1573 /*!
       
  1574     Returns the content of \a input decoded from RFC 2045 'encoded-word' format.
       
  1575 */
       
  1576 QString QMailMessageHeaderField::decodeWord(const QByteArray& input)
       
  1577 {
       
  1578     // This could actually be a sequence of encoded words...
       
  1579     return decodeWordSequence(input);
       
  1580 }
       
  1581 
       
  1582 /*!
       
  1583     Returns the content of the string \a input encoded into RFC 2231 'extended-parameter'
       
  1584     format.  The encoding used can be specified in \a charset, or can be deduced from the 
       
  1585     content of \a input if \a charset is empty.  If \a language is non-empty, it will be 
       
  1586     included in the encoded output; otherwise the language component will be extracted from 
       
  1587     \a charset, if it contains a trailing language specifier as defined in RFC 2231.
       
  1588 */
       
  1589 QByteArray QMailMessageHeaderField::encodeParameter(const QString& input, const QByteArray& charset, const QByteArray& language)
       
  1590 {
       
  1591     return ::encodeParameter(input, charset, language);
       
  1592 }
       
  1593 
       
  1594 /*!
       
  1595     Returns the content of \a input decoded from RFC 2231 'extended-parameter' format.
       
  1596 */
       
  1597 QString QMailMessageHeaderField::decodeParameter(const QByteArray& input)
       
  1598 {
       
  1599     return ::decodeParameter(input);
       
  1600 }
       
  1601 
       
  1602 /*!
       
  1603     Returns the content of the string \a input encoded into a sequence of RFC 2045 'encoded-word'
       
  1604     format tokens.  The encoding used can be specified in \a charset, or can be deduced for each
       
  1605     token read from \a input if \a charset is empty.
       
  1606 */
       
  1607 QByteArray QMailMessageHeaderField::encodeContent(const QString& input, const QByteArray& charset)
       
  1608 {
       
  1609     return encodeWordSequence(input, charset);
       
  1610 }
       
  1611 
       
  1612 /*!
       
  1613     Returns the content of \a input, decoding any encountered RFC 2045 'encoded-word' format
       
  1614     tokens to unicode.
       
  1615 */
       
  1616 QString QMailMessageHeaderField::decodeContent(const QByteArray& input)
       
  1617 {
       
  1618     return decodeWordSequence(input);
       
  1619 }
       
  1620 
       
  1621 /*!
       
  1622     Returns the content of \a input with any comment sections removed.
       
  1623 */
       
  1624 QByteArray QMailMessageHeaderField::removeComments(const QByteArray& input)
       
  1625 {
       
  1626     return ::removeComments(input, &::isprint);
       
  1627 }
       
  1628 
       
  1629 /*!
       
  1630     Returns the content of \a input with any whitespace characters removed. 
       
  1631     Whitespace inside double quotes is preserved.
       
  1632 */
       
  1633 QByteArray QMailMessageHeaderField::removeWhitespace(const QByteArray& input)
       
  1634 {
       
  1635     QByteArray result;
       
  1636     result.reserve(input.length());
       
  1637 
       
  1638     const char* const begin = input.constData();
       
  1639     const char* const end = begin + input.length();
       
  1640     const char* it = begin;
       
  1641     for (bool quoted = false; it != end; ++it) {
       
  1642         if (*it == '"') {
       
  1643             if ((it == begin) || (*(it - 1) != '\\'))
       
  1644                 quoted = !quoted;
       
  1645         }
       
  1646         if (quoted || !isspace(*it))
       
  1647             result.append(*it);
       
  1648     }
       
  1649     
       
  1650     return result;
       
  1651 }
       
  1652 
       
  1653 /*! \internal */
       
  1654 void QMailMessageHeaderField::output(QDataStream& out) const
       
  1655 {
       
  1656     impl(this)->output(out);
       
  1657 }
       
  1658 
       
  1659 /*! 
       
  1660     \fn QMailMessageHeaderField::serialize(Stream&) const
       
  1661     \internal 
       
  1662 */
       
  1663 template <typename Stream> 
       
  1664 void QMailMessageHeaderField::serialize(Stream &stream) const
       
  1665 {
       
  1666     impl(this)->serialize(stream);
       
  1667 }
       
  1668 
       
  1669 /*! 
       
  1670     \fn QMailMessageHeaderField::deserialize(Stream&)
       
  1671     \internal 
       
  1672 */
       
  1673 template <typename Stream> 
       
  1674 void QMailMessageHeaderField::deserialize(Stream &stream)
       
  1675 {
       
  1676     impl(this)->deserialize(stream);
       
  1677 }
       
  1678 
       
  1679 
       
  1680 /*!
       
  1681     \class QMailMessageContentType
       
  1682 
       
  1683     \brief The QMailMessageContentType class encapsulates the parsing of the RFC 2822
       
  1684     'Content-Type' header field.
       
  1685     
       
  1686     \ingroup messaginglibrary
       
  1687    
       
  1688     QMailMessageContentType provides simplified access to the various components of the 
       
  1689     'Content-Type' header field.
       
  1690     Components of the header field not exposed by member functions can be accessed using
       
  1691     the functions inherited from QMailMessageHeaderField.
       
  1692 */
       
  1693 
       
  1694 /*! \internal */
       
  1695 QMailMessageContentType::QMailMessageContentType()
       
  1696     : QMailMessageHeaderField("Content-Type")
       
  1697 {
       
  1698 }
       
  1699 
       
  1700 /*!
       
  1701     Creates a content type object from the data in \a type.
       
  1702 */
       
  1703 QMailMessageContentType::QMailMessageContentType(const QByteArray& type)
       
  1704     : QMailMessageHeaderField("Content-Type")
       
  1705 {
       
  1706     // Find the components, and create a content value from them
       
  1707     QByteArray content;
       
  1708 
       
  1709     // Although a conforming CT must be: <type> "/" <subtype> without whitespace,
       
  1710     // we'll be a bit more accepting
       
  1711     int index = type.indexOf('/');
       
  1712     if (index == -1)
       
  1713     {
       
  1714         content = type.trimmed();
       
  1715     }
       
  1716     else
       
  1717     {
       
  1718         QByteArray primaryType = type.left(index).trimmed();
       
  1719         QByteArray secondaryType = type.mid(index + 1).trimmed();
       
  1720 
       
  1721         content = primaryType;
       
  1722         if (!secondaryType.isEmpty())
       
  1723             content.append('/').append(secondaryType);
       
  1724     }
       
  1725 
       
  1726     parse(content, StructuredField);
       
  1727 }
       
  1728 
       
  1729 /*!
       
  1730     Creates a content type object from the content of \a field.
       
  1731 */
       
  1732 QMailMessageContentType::QMailMessageContentType(const QMailMessageHeaderField& field)
       
  1733     : QMailMessageHeaderField(field)
       
  1734 {
       
  1735     QMailMessageHeaderField::setId("Content-Type");
       
  1736 }
       
  1737 
       
  1738 /*!
       
  1739     Returns the primary type information of the content type header field.
       
  1740 
       
  1741     For example: if content() returns "text/plain", then type() returns "text"
       
  1742 */
       
  1743 QByteArray QMailMessageContentType::type() const
       
  1744 {
       
  1745     QByteArray entire = content();
       
  1746     int index = entire.indexOf('/');
       
  1747     if (index == -1)
       
  1748         return entire.trimmed();
       
  1749 
       
  1750     return entire.left(index).trimmed();
       
  1751 }
       
  1752 
       
  1753 /*!
       
  1754     Sets the primary type information of the 'Content-Type' header field to \a type. If \a type 
       
  1755     is empty, then any pre-existing sub-type information will be cleared.
       
  1756 
       
  1757     \sa setSubType()
       
  1758 */
       
  1759 void QMailMessageContentType::setType(const QByteArray& type)
       
  1760 {
       
  1761     if (type.isEmpty())
       
  1762     {
       
  1763         // Note - if there is a sub-type, setting type to null will destroy it
       
  1764         setContent(type);
       
  1765     }
       
  1766     else
       
  1767     {
       
  1768         QByteArray content(type);
       
  1769 
       
  1770         QByteArray secondaryType(subType());
       
  1771         if (!secondaryType.isEmpty())
       
  1772             content.append('/').append(secondaryType);
       
  1773 
       
  1774         setContent(content);
       
  1775     }
       
  1776 }
       
  1777 
       
  1778 /*!
       
  1779     Returns the sub-type information of the 'Content-Type' header field.
       
  1780 
       
  1781     For example: if content() returns "text/plain", then subType() returns "plain"
       
  1782 */
       
  1783 QByteArray QMailMessageContentType::subType() const
       
  1784 {
       
  1785     QByteArray entire = content();
       
  1786     int index = entire.indexOf('/');
       
  1787     if (index == -1)
       
  1788         return QByteArray();
       
  1789 
       
  1790     return entire.mid(index + 1).trimmed();
       
  1791 }
       
  1792 
       
  1793 /*!
       
  1794     Sets the sub-type information of the 'Content-Type' header field to \a subType. If no primary
       
  1795     type has been set, then setting the sub-type has no effect.
       
  1796 
       
  1797     \sa setType()
       
  1798 */
       
  1799 void QMailMessageContentType::setSubType(const QByteArray& subType)
       
  1800 {
       
  1801     QByteArray primaryType(type());
       
  1802     if (!primaryType.isEmpty())
       
  1803     {
       
  1804         if (!subType.isEmpty())
       
  1805             primaryType.append('/').append(subType);
       
  1806 
       
  1807         setContent(primaryType);
       
  1808     }
       
  1809 }
       
  1810 
       
  1811 /*!
       
  1812     Returns the value of the 'name' parameter, if present; otherwise returns an empty QByteArray.
       
  1813 */
       
  1814 QByteArray QMailMessageContentType::name() const
       
  1815 {
       
  1816     return parameter("name");
       
  1817 }
       
  1818 
       
  1819 /*!
       
  1820     Sets the value of the 'name' parameter to \a name.
       
  1821 */
       
  1822 void QMailMessageContentType::setName(const QByteArray& name)
       
  1823 {
       
  1824     setParameter("name", name);
       
  1825 }
       
  1826 
       
  1827 /*!
       
  1828     Returns the value of the 'boundary' parameter, if present; otherwise returns an empty QByteArray.
       
  1829 */
       
  1830 QByteArray QMailMessageContentType::boundary() const
       
  1831 {
       
  1832     QByteArray value = parameter("boundary");
       
  1833     if (value.isEmpty() || !isParameterEncoded("boundary"))
       
  1834         return value;
       
  1835 
       
  1836     // The boundary is an encoded parameter.  Therefore, we need to extract the
       
  1837     // usable ascii part, since a valid message must be composed of ascii only
       
  1838     return to7BitAscii(QMailMessageHeaderField::decodeParameter(value));
       
  1839 }
       
  1840 
       
  1841 /*!
       
  1842     Sets the value of the 'boundary' parameter to \a boundary.
       
  1843 */
       
  1844 void QMailMessageContentType::setBoundary(const QByteArray& boundary)
       
  1845 {
       
  1846     setParameter("boundary", boundary);
       
  1847 }
       
  1848 
       
  1849 /*!
       
  1850     Returns the value of the 'charset' parameter, if present; otherwise returns an empty QByteArray.
       
  1851 */
       
  1852 QByteArray QMailMessageContentType::charset() const
       
  1853 {
       
  1854     QByteArray value = parameter("charset");
       
  1855     if (value.isEmpty() || !isParameterEncoded("charset"))
       
  1856         return value;
       
  1857 
       
  1858     // The boundary is an encoded parameter.  Therefore, we need to extract the
       
  1859     // usable ascii part, since a valid charset must be composed of ascii only
       
  1860     return to7BitAscii(QMailMessageHeaderField::decodeParameter(value));
       
  1861 }
       
  1862 
       
  1863 /*!
       
  1864     Sets the value of the 'charset' parameter to \a charset.
       
  1865 */
       
  1866 void QMailMessageContentType::setCharset(const QByteArray& charset)
       
  1867 {
       
  1868     setParameter("charset", charset);
       
  1869 }
       
  1870 
       
  1871 
       
  1872 /*!
       
  1873     \class QMailMessageContentDisposition
       
  1874 
       
  1875     \brief The QMailMessageContentDisposition class encapsulates the parsing of the RFC 2822
       
  1876     'Content-Disposition' header field.
       
  1877     
       
  1878     \ingroup messaginglibrary
       
  1879    
       
  1880     QMailMessageContentDisposition provides simplified access to the various components of the 
       
  1881     'Content-Disposition' header field.
       
  1882     Components of the header field not exposed by member functions can be accessed using
       
  1883     the functions inherited from QMailMessageHeaderField.
       
  1884 */
       
  1885 
       
  1886 /*! \internal */
       
  1887 QMailMessageContentDisposition::QMailMessageContentDisposition()
       
  1888     : QMailMessageHeaderField("Content-Disposition")
       
  1889 {
       
  1890 }
       
  1891 
       
  1892 /*!
       
  1893     Creates a disposition header field object from the data in \a type.
       
  1894 */
       
  1895 QMailMessageContentDisposition::QMailMessageContentDisposition(const QByteArray& type)
       
  1896     : QMailMessageHeaderField("Content-Disposition", type)
       
  1897 {
       
  1898 }
       
  1899 
       
  1900 /*!
       
  1901     Creates a 'Content-Disposition' header field object with the type \a type.
       
  1902 */
       
  1903 QMailMessageContentDisposition::QMailMessageContentDisposition(QMailMessageContentDisposition::DispositionType type)
       
  1904     : QMailMessageHeaderField("Content-Disposition")
       
  1905 {
       
  1906     setType(type);
       
  1907 }
       
  1908 
       
  1909 /*!
       
  1910     Creates a disposition header field object from the content of \a field.
       
  1911 */
       
  1912 QMailMessageContentDisposition::QMailMessageContentDisposition(const QMailMessageHeaderField& field)
       
  1913     : QMailMessageHeaderField(field)
       
  1914 {
       
  1915     QMailMessageHeaderField::setId("Content-Disposition");
       
  1916 }
       
  1917 
       
  1918 /*!
       
  1919     Returns the disposition type of this header field.
       
  1920 */
       
  1921 QMailMessageContentDisposition::DispositionType QMailMessageContentDisposition::type() const
       
  1922 {
       
  1923     const QByteArray& type = content();
       
  1924 
       
  1925     if (insensitiveEqual(type, "inline"))
       
  1926         return Inline;
       
  1927     else if (insensitiveEqual(type, "attachment"))
       
  1928         return Attachment;
       
  1929 
       
  1930     return None;
       
  1931 }
       
  1932 
       
  1933 /*!
       
  1934     Sets the disposition type of this field to \a type.
       
  1935 */
       
  1936 void QMailMessageContentDisposition::setType(QMailMessageContentDisposition::DispositionType type)
       
  1937 {
       
  1938     if (type == Inline)
       
  1939         setContent("inline");
       
  1940     else if (type == Attachment)
       
  1941         setContent("attachment");
       
  1942     else
       
  1943         setContent(QByteArray());
       
  1944 }
       
  1945 
       
  1946 /*!
       
  1947     Returns the value of the 'filename' parameter, if present; otherwise returns an empty QByteArray.
       
  1948 */
       
  1949 QByteArray QMailMessageContentDisposition::filename() const
       
  1950 {
       
  1951     return parameter("filename");
       
  1952 }
       
  1953 
       
  1954 /*!
       
  1955     Sets the value of the 'filename' parameter to \a filename.
       
  1956 */
       
  1957 void QMailMessageContentDisposition::setFilename(const QByteArray& filename)
       
  1958 {
       
  1959     setParameter("filename", filename);
       
  1960 }
       
  1961 
       
  1962 /*!
       
  1963     Returns the value of the 'creation-date' parameter, if present; otherwise returns an uninitialised time stamp.
       
  1964 */
       
  1965 QMailTimeStamp QMailMessageContentDisposition::creationDate() const
       
  1966 {
       
  1967     return QMailTimeStamp(parameter("creation-date"));
       
  1968 }
       
  1969 
       
  1970 /*!
       
  1971     Sets the value of the 'creation-date' parameter to \a timeStamp.
       
  1972 */
       
  1973 void QMailMessageContentDisposition::setCreationDate(const QMailTimeStamp& timeStamp)
       
  1974 {
       
  1975     setParameter("creation-date", to7BitAscii(timeStamp.toString()));
       
  1976 }
       
  1977 
       
  1978 /*!
       
  1979     Returns the value of the 'modification-date' parameter, if present; otherwise returns an uninitialised time stamp.
       
  1980 */
       
  1981 QMailTimeStamp QMailMessageContentDisposition::modificationDate() const
       
  1982 {
       
  1983     return QMailTimeStamp(parameter("modification-date"));
       
  1984 }
       
  1985 
       
  1986 /*!
       
  1987     Sets the value of the 'modification-date' parameter to \a timeStamp.
       
  1988 */
       
  1989 void QMailMessageContentDisposition::setModificationDate(const QMailTimeStamp& timeStamp)
       
  1990 {
       
  1991     setParameter("modification-date", to7BitAscii(timeStamp.toString()));
       
  1992 }
       
  1993 
       
  1994 
       
  1995 /*!
       
  1996     Returns the value of the 'read-date' parameter, if present; otherwise returns an uninitialised time stamp.
       
  1997 */
       
  1998 QMailTimeStamp QMailMessageContentDisposition::readDate() const
       
  1999 {
       
  2000     return QMailTimeStamp(parameter("read-date"));
       
  2001 }
       
  2002 
       
  2003 /*!
       
  2004     Sets the value of the 'read-date' parameter to \a timeStamp.
       
  2005 */
       
  2006 void QMailMessageContentDisposition::setReadDate(const QMailTimeStamp& timeStamp)
       
  2007 {
       
  2008     setParameter("read-date", to7BitAscii(timeStamp.toString()));
       
  2009 }
       
  2010 
       
  2011 /*!
       
  2012     Returns the value of the 'size' parameter, if present; otherwise returns -1.
       
  2013 */
       
  2014 int QMailMessageContentDisposition::size() const
       
  2015 {
       
  2016     QByteArray sizeText = parameter("size");
       
  2017 
       
  2018     if (sizeText.isEmpty())
       
  2019         return -1;
       
  2020 
       
  2021     return sizeText.toUInt();
       
  2022 }
       
  2023 
       
  2024 /*!
       
  2025     Sets the value of the 'size' parameter to \a size.
       
  2026 */
       
  2027 void QMailMessageContentDisposition::setSize(int size)
       
  2028 {
       
  2029     setParameter("size", QByteArray::number(size));
       
  2030 }
       
  2031 
       
  2032 
       
  2033 /* QMailMessageHeader*/
       
  2034 
       
  2035 QMailMessageHeaderPrivate::QMailMessageHeaderPrivate()
       
  2036     : QPrivateImplementationBase(this)
       
  2037 {
       
  2038 }
       
  2039 
       
  2040 enum NewLineStatus { None, Cr, CrLf };
       
  2041 
       
  2042 static QList<QByteArray> parseHeaders(const QByteArray& input)
       
  2043 {
       
  2044     QList<QByteArray> result;
       
  2045     QByteArray progress;
       
  2046 
       
  2047     // Find each terminating newline, which must be CR, LF, then non-whitespace or end
       
  2048     NewLineStatus status = None;
       
  2049 
       
  2050     const char* begin = input.constData();
       
  2051     const char* it = begin;
       
  2052     for (const char* const end = it + input.length(); it != end; ++it) {
       
  2053         if (status == CrLf) {
       
  2054             if (*it == ' ' || *it == '\t') {
       
  2055                 // The CRLF was folded
       
  2056                 if ((it - begin) > 2) {
       
  2057                     progress.append(QByteArray(begin, (it - begin - 2)));
       
  2058                 }
       
  2059                 begin = it;
       
  2060             }
       
  2061             else {
       
  2062                 // That was an unescaped CRLF
       
  2063                 if ((it - begin) > 2) {
       
  2064                     progress.append(QByteArray(begin, (it - begin) - 2));
       
  2065                 }
       
  2066                 if (!progress.isEmpty()) {
       
  2067                     // Non-empty field
       
  2068                     result.append(progress);
       
  2069                     progress.clear();
       
  2070                 }
       
  2071                 begin = it;
       
  2072             }
       
  2073             status = None;
       
  2074         }
       
  2075         else if (status == Cr) {
       
  2076             if (*it == QMailMessage::LineFeed) {
       
  2077                 // CRLF sequence completed
       
  2078                 status = CrLf;
       
  2079             }
       
  2080             else {
       
  2081                 status = None;
       
  2082             }
       
  2083         }
       
  2084         else {
       
  2085             if (*it == QMailMessage::CarriageReturn)
       
  2086                 status = Cr;
       
  2087         }
       
  2088     }
       
  2089 
       
  2090     if (it != begin) {
       
  2091         int skip = (status == CrLf ? 2 : (status == None ? 0 : 1));
       
  2092         if ((it - begin) > skip) {
       
  2093             progress.append(QByteArray(begin, (it - begin) - skip));
       
  2094         }
       
  2095         if (!progress.isEmpty()) {
       
  2096             result.append(progress);
       
  2097         }
       
  2098     }
       
  2099 
       
  2100     return result;
       
  2101 }
       
  2102 
       
  2103 QMailMessageHeaderPrivate::QMailMessageHeaderPrivate(const QByteArray& input)
       
  2104     : QPrivateImplementationBase(this),
       
  2105       _headerFields(parseHeaders(input))
       
  2106 {
       
  2107 }
       
  2108 
       
  2109 static QByteArray fieldId(const QByteArray &id)
       
  2110 {
       
  2111     QByteArray name = id.trimmed();
       
  2112     if ( !name.endsWith(':') )
       
  2113         name.append(':');
       
  2114     return name;
       
  2115 }
       
  2116 
       
  2117 static QPair<QByteArray, QByteArray> fieldParts(const QByteArray &id, const QByteArray &content)
       
  2118 {
       
  2119     QByteArray value = QByteArray(" ") + content.trimmed();
       
  2120     return qMakePair(fieldId(id), value);
       
  2121 }
       
  2122 
       
  2123 static bool matchingId(const QByteArray& id, const QByteArray& other, bool allowPartial = false)
       
  2124 {
       
  2125     QByteArray match(id.trimmed());
       
  2126 
       
  2127     int index = insensitiveIndexOf(match, other);
       
  2128     if (index == -1)
       
  2129         return false;
       
  2130 
       
  2131     if (index > 0)
       
  2132     {
       
  2133         // Ensure that every preceding character is whitespace
       
  2134         QByteArray leader(other.left(index).trimmed());
       
  2135         if (!leader.isEmpty())
       
  2136             return false;
       
  2137     }
       
  2138 
       
  2139     if (allowPartial)
       
  2140         return true;
       
  2141 
       
  2142     int lastIndex = index + match.length() - 1;
       
  2143     index = other.indexOf(':', lastIndex);
       
  2144     if (index == -1)
       
  2145         index = other.length() - 1;
       
  2146 
       
  2147     // Ensure that there is only whitespace between the matched ID and the end of the ID
       
  2148     if ((index - lastIndex) > 1)
       
  2149     {
       
  2150         QByteArray trailer(other.mid(lastIndex + 1, (index - lastIndex)).trimmed());
       
  2151         if (!trailer.isEmpty())
       
  2152             return false;
       
  2153     }
       
  2154 
       
  2155     return true;
       
  2156 }
       
  2157 
       
  2158 void QMailMessageHeaderPrivate::update(const QByteArray &id, const QByteArray &content)
       
  2159 {
       
  2160     QPair<QByteArray, QByteArray> parts = fieldParts(id, content);
       
  2161     QByteArray updated = parts.first + parts.second;
       
  2162 
       
  2163     const QList<QByteArray>::Iterator end = _headerFields.end();
       
  2164     for (QList<QByteArray>::Iterator it = _headerFields.begin(); it != end; ++it) {
       
  2165         if ( matchingId(id, (*it)) ) {
       
  2166             *it = updated;
       
  2167             return;
       
  2168         }
       
  2169     }
       
  2170 
       
  2171     // new header field, add it
       
  2172     _headerFields.append( updated );
       
  2173 }
       
  2174 
       
  2175 void QMailMessageHeaderPrivate::append(const QByteArray &id, const QByteArray &content)
       
  2176 {
       
  2177     QPair<QByteArray, QByteArray> parts = fieldParts(id, content);
       
  2178     _headerFields.append( parts.first + parts.second );
       
  2179 }
       
  2180 
       
  2181 void QMailMessageHeaderPrivate::remove(const QByteArray &id)
       
  2182 {
       
  2183     QList<QList<QByteArray>::Iterator> matches;
       
  2184 
       
  2185     const QList<QByteArray>::Iterator end = _headerFields.end();
       
  2186     for (QList<QByteArray>::Iterator it = _headerFields.begin(); it != end; ++it) {
       
  2187         if ( matchingId(id, (*it)) )
       
  2188             matches.prepend(it);
       
  2189     }
       
  2190 
       
  2191     foreach (QList<QByteArray>::Iterator it, matches)
       
  2192         _headerFields.erase(it);
       
  2193 }
       
  2194 
       
  2195 QList<QMailMessageHeaderField> QMailMessageHeaderPrivate::fields(const QByteArray& id, int maximum) const
       
  2196 {
       
  2197     QList<QMailMessageHeaderField> result;
       
  2198 
       
  2199     foreach (const QByteArray& field, _headerFields) {
       
  2200         QMailMessageHeaderField headerField(field, QMailMessageHeaderField::UnstructuredField);
       
  2201         if ( matchingId(id, headerField.id()) ) {
       
  2202             result.append(headerField);
       
  2203             if (maximum > 0 && result.count() == maximum)
       
  2204                 return result;
       
  2205         }
       
  2206     }
       
  2207 
       
  2208     return result;
       
  2209 }
       
  2210 
       
  2211 void QMailMessageHeaderPrivate::output(QDataStream& out, const QList<QByteArray>& exclusions, bool excludeInternalFields) const
       
  2212 {
       
  2213     foreach (const QByteArray& field, _headerFields) {
       
  2214         QMailMessageHeaderField headerField(field, QMailMessageHeaderField::UnstructuredField);
       
  2215         const QByteArray& id = headerField.id();
       
  2216         bool excluded = false;
       
  2217 
       
  2218         // Bypass any header field that has the internal prefix
       
  2219         if (excludeInternalFields)
       
  2220             excluded = matchingId(internalPrefix(), id, true);
       
  2221 
       
  2222         // Bypass any header in the list of exclusions
       
  2223         if (!excluded)
       
  2224             foreach (const QByteArray& exclusion, exclusions)
       
  2225                 if (matchingId(exclusion, id))
       
  2226                     excluded = true;
       
  2227 
       
  2228         if (!excluded)
       
  2229             headerField.output(out);
       
  2230     }
       
  2231 }
       
  2232 
       
  2233 template <typename Stream> 
       
  2234 void QMailMessageHeaderPrivate::serialize(Stream &stream) const
       
  2235 {
       
  2236     stream << _headerFields;
       
  2237 }
       
  2238 
       
  2239 template <typename Stream> 
       
  2240 void QMailMessageHeaderPrivate::deserialize(Stream &stream)
       
  2241 {
       
  2242     stream >> _headerFields;
       
  2243 }
       
  2244 
       
  2245 
       
  2246 /*!
       
  2247     \class QMailMessageHeader
       
  2248     \internal
       
  2249 */
       
  2250 
       
  2251 QMailMessageHeader::QMailMessageHeader()
       
  2252     : QPrivatelyImplemented<QMailMessageHeaderPrivate>(new QMailMessageHeaderPrivate())
       
  2253 {
       
  2254 }
       
  2255 
       
  2256 QMailMessageHeader::QMailMessageHeader(const QByteArray& input)
       
  2257     : QPrivatelyImplemented<QMailMessageHeaderPrivate>(new QMailMessageHeaderPrivate(input))
       
  2258 {
       
  2259 }
       
  2260 
       
  2261 void QMailMessageHeader::update(const QByteArray &id, const QByteArray &content)
       
  2262 {
       
  2263     impl(this)->update(id, content);
       
  2264 }
       
  2265 
       
  2266 void QMailMessageHeader::append(const QByteArray &id, const QByteArray &content)
       
  2267 {
       
  2268     impl(this)->append(id, content);
       
  2269 }
       
  2270 
       
  2271 void QMailMessageHeader::remove(const QByteArray &id)
       
  2272 {
       
  2273     impl(this)->remove(id);
       
  2274 }
       
  2275 
       
  2276 QMailMessageHeaderField QMailMessageHeader::field(const QByteArray& id) const
       
  2277 {
       
  2278     QList<QMailMessageHeaderField> result = impl(this)->fields(id, 1);
       
  2279     if (result.count())
       
  2280         return result[0];
       
  2281 
       
  2282     return QMailMessageHeaderField();
       
  2283 }
       
  2284 
       
  2285 QList<QMailMessageHeaderField> QMailMessageHeader::fields(const QByteArray& id) const
       
  2286 {
       
  2287     return impl(this)->fields(id);
       
  2288 }
       
  2289 
       
  2290 QList<const QByteArray*> QMailMessageHeader::fieldList() const
       
  2291 {
       
  2292     QList<const QByteArray*> result;
       
  2293 
       
  2294     QList<QByteArray>::ConstIterator const end = impl(this)->_headerFields.end();
       
  2295     for (QList<QByteArray>::ConstIterator it = impl(this)->_headerFields.begin(); it != end; ++it)
       
  2296         result.append(&(*it));
       
  2297 
       
  2298     return result;
       
  2299 }
       
  2300 
       
  2301 void QMailMessageHeader::output(QDataStream& out, const QList<QByteArray>& exclusions, bool excludeInternalFields) const
       
  2302 {
       
  2303     impl(this)->output(out, exclusions, excludeInternalFields);
       
  2304 }
       
  2305 
       
  2306 /*! 
       
  2307     \fn QMailMessageHeader::serialize(Stream&) const
       
  2308     \internal 
       
  2309 */
       
  2310 template <typename Stream> 
       
  2311 void QMailMessageHeader::serialize(Stream &stream) const
       
  2312 {
       
  2313     impl(this)->serialize(stream);
       
  2314 }
       
  2315 
       
  2316 /*! 
       
  2317     \fn QMailMessageHeader::deserialize(Stream&)
       
  2318     \internal 
       
  2319 */
       
  2320 template <typename Stream> 
       
  2321 void QMailMessageHeader::deserialize(Stream &stream)
       
  2322 {
       
  2323     impl(this)->deserialize(stream);
       
  2324 }
       
  2325 
       
  2326 
       
  2327 /* QMailMessageBody */
       
  2328 
       
  2329 QMailMessageBodyPrivate::QMailMessageBodyPrivate()
       
  2330     : QPrivateImplementationBase(this)
       
  2331 {
       
  2332     // Default encoding
       
  2333     _encoding = QMailMessageBody::SevenBit;
       
  2334 }
       
  2335 
       
  2336 void QMailMessageBodyPrivate::fromLongString(LongString& ls, const QMailMessageContentType& content, QMailMessageBody::TransferEncoding te, QMailMessageBody::EncodingStatus status)
       
  2337 {
       
  2338     _encoding = te;
       
  2339     _type = content;
       
  2340     _encoded = (status == QMailMessageBody::AlreadyEncoded);
       
  2341     _filename = QString();
       
  2342     _bodyData = ls;
       
  2343 }
       
  2344 
       
  2345 void QMailMessageBodyPrivate::fromFile(const QString& file, const QMailMessageContentType& content, QMailMessageBody::TransferEncoding te, QMailMessageBody::EncodingStatus status)
       
  2346 {
       
  2347     _encoding = te;
       
  2348     _type = content;
       
  2349     _encoded = (status == QMailMessageBody::AlreadyEncoded);
       
  2350     _filename = file;
       
  2351     _bodyData = LongString(file);
       
  2352 }
       
  2353 
       
  2354 void QMailMessageBodyPrivate::fromStream(QDataStream& in, const QMailMessageContentType& content, QMailMessageBody::TransferEncoding te, QMailMessageBody::EncodingStatus status)
       
  2355 {
       
  2356     _encoding = te;
       
  2357     _type = content;
       
  2358     _encoded = true;
       
  2359     _filename = QString();
       
  2360     _bodyData = LongString();
       
  2361     
       
  2362     // If the data is already encoded, we don't need to do it again
       
  2363     if (status == QMailMessageBody::AlreadyEncoded)
       
  2364         te = QMailMessageBody::SevenBit;
       
  2365 
       
  2366     QMailCodec* codec = codecForEncoding(te, content);
       
  2367     if (codec)
       
  2368     {
       
  2369         // Stream to the buffer, encoding as required
       
  2370         QByteArray encoded;
       
  2371         {
       
  2372             QDataStream out(&encoded, QIODevice::WriteOnly);
       
  2373             codec->encode(out, in);
       
  2374         }
       
  2375         _bodyData = LongString(encoded);
       
  2376         delete codec;
       
  2377     }
       
  2378 }
       
  2379 
       
  2380 void QMailMessageBodyPrivate::fromStream(QTextStream& in, const QMailMessageContentType& content, QMailMessageBody::TransferEncoding te)
       
  2381 {
       
  2382     _encoding = te;
       
  2383     _type = content;
       
  2384     _encoded = true;
       
  2385     _filename = QString();
       
  2386     _bodyData = LongString();
       
  2387 
       
  2388     QMailCodec* codec = codecForEncoding(te, content);
       
  2389     if (codec)
       
  2390     {
       
  2391         QByteArray encoded;
       
  2392         {
       
  2393             QDataStream out(&encoded, QIODevice::WriteOnly);
       
  2394 
       
  2395             // Convert the unicode string to a byte-stream, via the nominated character set
       
  2396             QString charset = _type.charset();
       
  2397 
       
  2398             // If no character set is specified - treat the data as UTF-8; since it is
       
  2399             // textual data, it must have some character set...
       
  2400             if (charset.isEmpty())
       
  2401                 charset = "UTF-8";
       
  2402 
       
  2403             codec->encode(out, in, charset);
       
  2404         }
       
  2405         _bodyData = LongString(encoded);
       
  2406         delete codec;
       
  2407     }
       
  2408 }
       
  2409 
       
  2410 static bool unicodeConvertingCharset(const QByteArray& charset)
       
  2411 {
       
  2412     // See if this is a unicode-capable codec
       
  2413     if (QTextCodec* textCodec = codecForName(charset, true))
       
  2414     {
       
  2415         const QChar multiByteChar = 0x1234;
       
  2416         return textCodec->canEncode(multiByteChar);
       
  2417     }
       
  2418     else
       
  2419     {
       
  2420         qWarning() << "unicodeConvertingCharset: unable to find codec for charset:" << charset;
       
  2421     }
       
  2422 
       
  2423     return false;
       
  2424 }
       
  2425 
       
  2426 static QByteArray extractionCharset(const QMailMessageContentType& type)
       
  2427 {
       
  2428     QByteArray charset;
       
  2429 
       
  2430     // Find the charset for this data, if it is text data
       
  2431     if (insensitiveEqual(type.type(), "text"))
       
  2432     {
       
  2433         charset = type.charset();
       
  2434         if (!charset.isEmpty())
       
  2435         {
       
  2436             // If the codec can't handle multi-byte characters, don't extract to/from unicode
       
  2437             if (!unicodeConvertingCharset(charset))
       
  2438                 charset = QByteArray();
       
  2439         }
       
  2440     }
       
  2441 
       
  2442     return charset;
       
  2443 }
       
  2444 
       
  2445 bool QMailMessageBodyPrivate::toFile(const QString& file, QMailMessageBody::EncodingFormat format) const
       
  2446 {
       
  2447     QFile outFile(file);
       
  2448     if (!outFile.open(QIODevice::WriteOnly))
       
  2449     {
       
  2450         qWarning() << "Unable to open for write:" << file;
       
  2451         return false;
       
  2452     }
       
  2453 
       
  2454     bool encodeOutput = (format == QMailMessageBody::Encoded);
       
  2455 
       
  2456     // Find the charset for this data, if it is text data
       
  2457     QByteArray charset(extractionCharset(_type));
       
  2458 
       
  2459     QMailMessageBody::TransferEncoding te = _encoding;
       
  2460 
       
  2461     // If our data is in the required condition, we don't need to encode/decode
       
  2462     if (encodeOutput == _encoded)
       
  2463         te = QMailMessageBody::Binary;
       
  2464 
       
  2465     QMailCodec* codec = codecForEncoding(te, _type);
       
  2466     if (codec)
       
  2467     {
       
  2468         bool result = false;
       
  2469 
       
  2470         // Empty charset indicates no unicode encoding; encoded return data means binary streams
       
  2471         if (charset.isEmpty() || encodeOutput)
       
  2472         {
       
  2473             // We are dealing with binary data
       
  2474             QDataStream out(&outFile);
       
  2475             QDataStream* in = _bodyData.dataStream();
       
  2476             if (encodeOutput)
       
  2477                 codec->encode(out, *in);
       
  2478             else
       
  2479                 codec->decode(out, *in);
       
  2480             result = (in->status() == QDataStream::Ok);
       
  2481             delete in;
       
  2482         }
       
  2483         else // we should probably check that charset matches this->charset
       
  2484         {
       
  2485             // We are dealing with unicode text data, which we want in unencoded form
       
  2486             QTextStream out(&outFile);
       
  2487             out.setCodec(charset);
       
  2488 
       
  2489             // If the content is unencoded we can pass it back via a text stream
       
  2490             if (!_encoded)
       
  2491             {
       
  2492                 QTextStream* in = _bodyData.textStream();
       
  2493                 in->setCodec(charset);
       
  2494                 QMailCodec::copy(out, *in);
       
  2495                 result = (in->status() == QTextStream::Ok);
       
  2496                 delete in;
       
  2497             }
       
  2498             else
       
  2499             {
       
  2500                 QDataStream* in = _bodyData.dataStream();
       
  2501                 codec->decode(out, *in, charset);
       
  2502                 result = (in->status() == QDataStream::Ok);
       
  2503                 delete in;
       
  2504             }
       
  2505         }
       
  2506 
       
  2507         delete codec;
       
  2508         return result;
       
  2509     }
       
  2510 
       
  2511     return false;
       
  2512 }
       
  2513 
       
  2514 bool QMailMessageBodyPrivate::toStream(QDataStream& out, QMailMessageBody::EncodingFormat format) const
       
  2515 {
       
  2516     bool encodeOutput = (format == QMailMessageBody::Encoded);
       
  2517     QMailMessageBody::TransferEncoding te = _encoding;
       
  2518 
       
  2519     // If our data is in the required condition, we don't need to encode/decode
       
  2520     if (encodeOutput == _encoded)
       
  2521         te = QMailMessageBody::Binary;
       
  2522 
       
  2523     QMailCodec* codec = codecForEncoding(te, _type);
       
  2524     if (codec)
       
  2525     {
       
  2526         bool result = false;
       
  2527 
       
  2528         QByteArray charset(extractionCharset(_type));
       
  2529         if (!charset.isEmpty() && !_filename.isEmpty() && encodeOutput)
       
  2530         {
       
  2531             // This data must be unicode in the file
       
  2532             QTextStream* in = _bodyData.textStream();
       
  2533             in->setCodec(charset);
       
  2534             codec->encode(out, *in, charset);
       
  2535             result = (in->status() == QTextStream::Ok);
       
  2536             delete in;
       
  2537         }
       
  2538         else
       
  2539         {
       
  2540             QDataStream* in = _bodyData.dataStream();
       
  2541             if (encodeOutput)
       
  2542                 codec->encode(out, *in);
       
  2543             else
       
  2544                 codec->decode(out, *in);
       
  2545             result = (in->status() == QDataStream::Ok);
       
  2546             delete in;
       
  2547         }
       
  2548 
       
  2549         delete codec;
       
  2550         return result;
       
  2551     }
       
  2552 
       
  2553     return false;
       
  2554 }
       
  2555 
       
  2556 bool QMailMessageBodyPrivate::toStream(QTextStream& out) const
       
  2557 {
       
  2558     QByteArray charset = _type.charset();
       
  2559     if (charset.isEmpty() || (insensitiveIndexOf("ascii", charset) != -1)) {
       
  2560         // We'll assume the text is plain ASCII, to be extracted to Latin-1
       
  2561         charset = "ISO-8859-1";
       
  2562     }
       
  2563 
       
  2564     out.setCodec(charset);
       
  2565 
       
  2566     QMailMessageBody::TransferEncoding te = _encoding;
       
  2567 
       
  2568     // If our data is not encoded, we don't need to decode
       
  2569     if (!_encoded)
       
  2570         te = QMailMessageBody::Binary;
       
  2571 
       
  2572     QMailCodec* codec = codecForEncoding(te, _type);
       
  2573     if (codec)
       
  2574     {
       
  2575         bool result = false;
       
  2576 
       
  2577         if (!_encoded && !_filename.isEmpty() && unicodeConvertingCharset(charset))
       
  2578         { 
       
  2579             // The data is already in unicode format
       
  2580             QTextStream* in = _bodyData.textStream();
       
  2581             in->setCodec(charset);
       
  2582             QMailCodec::copy(out, *in);
       
  2583             result = (in->status() == QTextStream::Ok);
       
  2584             delete in;
       
  2585         }
       
  2586         else
       
  2587         {
       
  2588             // Write the data to out, decoding if necessary
       
  2589             QDataStream* in = _bodyData.dataStream();
       
  2590             codec->decode(out, *in, charset);
       
  2591             result = (in->status() == QDataStream::Ok);
       
  2592             delete in;
       
  2593         }
       
  2594 
       
  2595         delete codec;
       
  2596         return result;
       
  2597     }
       
  2598 
       
  2599     return false;
       
  2600 }
       
  2601 
       
  2602 QMailMessageContentType QMailMessageBodyPrivate::contentType() const
       
  2603 {
       
  2604     return _type;
       
  2605 }
       
  2606 
       
  2607 QMailMessageBody::TransferEncoding QMailMessageBodyPrivate::transferEncoding() const
       
  2608 {
       
  2609     return _encoding;
       
  2610 }
       
  2611 
       
  2612 bool QMailMessageBodyPrivate::isEmpty() const
       
  2613 {
       
  2614     return _bodyData.isEmpty();
       
  2615 }
       
  2616 
       
  2617 int QMailMessageBodyPrivate::length() const
       
  2618 {
       
  2619     return _bodyData.length();
       
  2620 }
       
  2621 
       
  2622 uint QMailMessageBodyPrivate::indicativeSize() const
       
  2623 {
       
  2624     return (_bodyData.length() / IndicativeSizeUnit);
       
  2625 }
       
  2626 
       
  2627 void QMailMessageBodyPrivate::output(QDataStream& out, bool includeAttachments) const
       
  2628 {
       
  2629     if ( includeAttachments )
       
  2630         toStream( out, QMailMessageBody::Encoded );
       
  2631 }
       
  2632 
       
  2633 template <typename Stream> 
       
  2634 void QMailMessageBodyPrivate::serialize(Stream &stream) const
       
  2635 {
       
  2636     stream << _encoding;
       
  2637     stream << _bodyData;
       
  2638     stream << _filename;
       
  2639     stream << _encoded;
       
  2640     stream << _type;
       
  2641 }
       
  2642 
       
  2643 template <typename Stream> 
       
  2644 void QMailMessageBodyPrivate::deserialize(Stream &stream)
       
  2645 {
       
  2646     stream >> _encoding;
       
  2647     stream >> _bodyData;
       
  2648     stream >> _filename;
       
  2649     stream >> _encoded;
       
  2650     stream >> _type;
       
  2651 }
       
  2652 
       
  2653 
       
  2654 /*!
       
  2655     \class QMailMessageBody
       
  2656 
       
  2657     \brief The QMailMessageBody class contains the body element of a message or message part.
       
  2658     
       
  2659     \ingroup messaginglibrary
       
  2660    
       
  2661     The body of a message or message part is treated as an atomic unit by the Qt Extended messaging library.  It can only be inserted into a message part container or extracted
       
  2662     from one.  It can be inserted or extracted using either a QByteArray, a QDataStream
       
  2663     or to/from a file.  In the case of unicode text data, the insertion and extraction can
       
  2664     operate on either a QString, a QTextStream or to/from a file.
       
  2665 
       
  2666     The body data must be associated with a QMailMessageContentType describing that data.
       
  2667     When extracting body data from a message or part to unicode text, the content type
       
  2668     description must include a parameter named 'charset'; this parameter is used to locate
       
  2669     a QTextCodec to be used to extract unicode data from the body data octet stream.  
       
  2670     
       
  2671     If the Content-Type of the data is a subtype of "text", then line-ending translation 
       
  2672     will be used to ensure that the text is transmitted with CR/LF line endings.  The text 
       
  2673     data supplied to QMailMessageBody must conform to the RFC 2822 restrictions on maximum 
       
  2674     line lengths: "Each line of characters MUST be no more than 998 characters, and SHOULD 
       
  2675     be no more than 78 characters, excluding the CRLF."  Textual message body data decoded 
       
  2676     from a QMailMessageBody object will have transmitted CR/LF line endings converted to 
       
  2677     \c \n on extraction.
       
  2678 
       
  2679     The body data can also be encoded from 8-bit octets to 7-bit ASCII characters for
       
  2680     safe transmission through obsolete email systems.  When creating an instance of the 
       
  2681     QMailMessageBody class, the encoding to be used must be specified using the 
       
  2682     QMailMessageBody::TransferEncoding enum.
       
  2683 
       
  2684     \sa QMailMessagePart, QMailMessage, QTextCodec
       
  2685 */    
       
  2686 
       
  2687 /*!
       
  2688     \typedef QMailMessageBody::ImplementationType
       
  2689     \internal
       
  2690 */
       
  2691 
       
  2692 /*! 
       
  2693     Creates an instance of QMailMessageBody.
       
  2694 */
       
  2695 QMailMessageBody::QMailMessageBody()
       
  2696     : QPrivatelyImplemented<QMailMessageBodyPrivate>(new QMailMessageBodyPrivate())
       
  2697 {
       
  2698 }
       
  2699 
       
  2700 /*!
       
  2701     Creates a message body from the data contained in the file \a filename, having the content type 
       
  2702     \a type.  If \a status is QMailMessageBody::RequiresEncoding, the data from the file will be 
       
  2703     encoded to \a encoding for transmission; otherwise it must already be in that encoding, which
       
  2704     will be reported to recipients of the data.
       
  2705 
       
  2706     If \a type is a subtype of "text", the data will be treated as text, and line-ending
       
  2707     translation will be employed.  Otherwise, the file will be treated as containing binary 
       
  2708     data.  If the file contains unicode text data, it will be converted to an octet stream using
       
  2709     a QTextCodec object identified by the 'charset' parameter of \a type.
       
  2710 
       
  2711     If \a encoding is QMailMessageBody::QuotedPrintable, encoding will be performed assuming
       
  2712     conformance to RFC 2045.
       
  2713 
       
  2714     Note that the data is not actually read from the file until it is requested by another function.
       
  2715 
       
  2716     \sa QMailCodec, QMailQuotedPrintableCodec, QMailMessageContentType, QTextCodec
       
  2717 */
       
  2718 QMailMessageBody QMailMessageBody::fromFile(const QString& filename, const QMailMessageContentType& type, TransferEncoding encoding, EncodingStatus status)
       
  2719 {
       
  2720     QMailMessageBody body;
       
  2721     body.impl<QMailMessageBodyPrivate>()->fromFile(filename, type, encoding, status);
       
  2722     return body;
       
  2723 }
       
  2724 
       
  2725 /*!
       
  2726     Creates a message body from the data read from \a in, having the content type \a type.  
       
  2727     If \a status is QMailMessageBody::RequiresEncoding, the data from the file will be 
       
  2728     encoded to \a encoding for transmission; otherwise it must already be in that encoding, 
       
  2729     which will be reported to recipients of the data.
       
  2730 
       
  2731     If \a type is a subtype of "text", the data will be treated as text, and line-ending
       
  2732     translation will be employed.  Otherwise, the file will be treated as containing binary 
       
  2733     data.
       
  2734 
       
  2735     If \a encoding is QMailMessageBody::QuotedPrintable, encoding will be performed assuming
       
  2736     conformance to RFC 2045.
       
  2737 
       
  2738     \sa QMailCodec, QMailQuotedPrintableCodec
       
  2739 */
       
  2740 QMailMessageBody QMailMessageBody::fromStream(QDataStream& in, const QMailMessageContentType& type, TransferEncoding encoding, EncodingStatus status)
       
  2741 {
       
  2742     QMailMessageBody body;
       
  2743     body.impl<QMailMessageBodyPrivate>()->fromStream(in, type, encoding, status);
       
  2744     return body;
       
  2745 }
       
  2746 
       
  2747 /*!
       
  2748     Creates a message body from the data contained in \a input, having the content type 
       
  2749     \a type.  If \a status is QMailMessageBody::RequiresEncoding, the data from the file will be 
       
  2750     encoded to \a encoding for transmission; otherwise it must already be in that encoding, 
       
  2751     which will be reported to recipients of the data.
       
  2752 
       
  2753     If \a type is a subtype of "text", the data will be treated as text, and line-ending
       
  2754     translation will be employed.  Otherwise, the file will be treated as containing binary 
       
  2755     data.
       
  2756 
       
  2757     If \a encoding is QMailMessageBody::QuotedPrintable, encoding will be performed assuming
       
  2758     conformance to RFC 2045.
       
  2759 
       
  2760     \sa QMailCodec, QMailQuotedPrintableCodec
       
  2761 */
       
  2762 QMailMessageBody QMailMessageBody::fromData(const QByteArray& input, const QMailMessageContentType& type, TransferEncoding encoding, EncodingStatus status)
       
  2763 {
       
  2764     QMailMessageBody body;
       
  2765     {
       
  2766         QDataStream in(input);
       
  2767         body.impl<QMailMessageBodyPrivate>()->fromStream(in, type, encoding, status);
       
  2768     }
       
  2769     return body;
       
  2770 }
       
  2771 
       
  2772 /*!
       
  2773     Creates a message body from the data read from \a in, having the content type \a type.  
       
  2774     The data read from \a in will be encoded to \a encoding for transmission, and line-ending
       
  2775     translation will be employed.  The unicode text data will be converted to an octet stream 
       
  2776     using a QTextCodec object identified by the 'charset' parameter of \a type.
       
  2777 
       
  2778     If \a encoding is QMailMessageBody::QuotedPrintable, encoding will be performed assuming
       
  2779     conformance to RFC 2045.
       
  2780 
       
  2781     \sa QMailCodec, QMailQuotedPrintableCodec, QMailMessageContentType, QTextCodec
       
  2782 */
       
  2783 QMailMessageBody QMailMessageBody::fromStream(QTextStream& in, const QMailMessageContentType& type, TransferEncoding encoding)
       
  2784 {
       
  2785     QMailMessageBody body;
       
  2786     body.impl<QMailMessageBodyPrivate>()->fromStream(in, type, encoding);
       
  2787     return body;
       
  2788 }
       
  2789 
       
  2790 /*!
       
  2791     Creates a message body from the data contained in \a input, having the content type 
       
  2792     \a type.  The data from \a input will be encoded to \a encoding for transmission, and 
       
  2793     line-ending translation will be employed.  The unicode text data will be converted to 
       
  2794     an octet stream using a QTextCodec object identified by the 'charset' parameter of \a type.
       
  2795 
       
  2796     If \a encoding is QMailMessageBody::QuotedPrintable, encoding will be performed assuming
       
  2797     conformance to RFC 2045.
       
  2798 
       
  2799     \sa QMailCodec, QMailMessageContentType, QTextCodec
       
  2800 */
       
  2801 QMailMessageBody QMailMessageBody::fromData(const QString& input, const QMailMessageContentType& type, TransferEncoding encoding)
       
  2802 {
       
  2803     QMailMessageBody body;
       
  2804     {
       
  2805         QTextStream in(const_cast<QString*>(&input), QIODevice::ReadOnly);
       
  2806         body.impl<QMailMessageBodyPrivate>()->fromStream(in, type, encoding);
       
  2807     }
       
  2808     return body;
       
  2809 }
       
  2810 
       
  2811 QMailMessageBody QMailMessageBody::fromLongString(LongString& ls, const QMailMessageContentType& type, TransferEncoding encoding, EncodingStatus status)
       
  2812 {
       
  2813     QMailMessageBody body;
       
  2814     {
       
  2815         body.impl<QMailMessageBodyPrivate>()->fromLongString(ls, type, encoding, status);
       
  2816     }
       
  2817     return body;
       
  2818 }
       
  2819 
       
  2820 /*!
       
  2821     Writes the data of the message body to the file named \a filename.  If \a format is
       
  2822     QMailMessageBody::Encoded, then the data is written in the transfer encoding it was
       
  2823     created with; otherwise, it is written in unencoded form.
       
  2824 
       
  2825     If the body has a content type with a QMailMessageContentType::type() of "text", and the 
       
  2826     content type parameter 'charset' is not empty, then the unencoded data will be written 
       
  2827     as unicode text data, using the charset parameter to locate the appropriate QTextCodec.
       
  2828 
       
  2829     Returns false if the operation causes an error; otherwise returns true.
       
  2830 
       
  2831     \sa QMailCodec, QMailMessageContentType, QTextCodec
       
  2832 */
       
  2833 bool QMailMessageBody::toFile(const QString& filename, EncodingFormat format) const
       
  2834 {
       
  2835     return impl(this)->toFile(filename, format);
       
  2836 }
       
  2837 
       
  2838 /*!
       
  2839     Returns the data of the message body as a QByteArray.  If \a format is
       
  2840     QMailMessageBody::Encoded, then the data is written in the transfer encoding it was
       
  2841     created with; otherwise, it is written in unencoded form.
       
  2842 
       
  2843     \sa QMailCodec
       
  2844 */
       
  2845 QByteArray QMailMessageBody::data(EncodingFormat format) const
       
  2846 {
       
  2847     QByteArray result;
       
  2848     {
       
  2849         QDataStream out(&result, QIODevice::WriteOnly);
       
  2850         impl(this)->toStream(out, format);
       
  2851     }
       
  2852     return result;
       
  2853 }
       
  2854 
       
  2855 /*!
       
  2856     Writes the data of the message body to the stream \a out. If \a format is
       
  2857     QMailMessageBody::Encoded, then the data is written in the transfer encoding it was
       
  2858     created with; otherwise, it is written in unencoded form.
       
  2859 
       
  2860     Returns false if the operation causes an error; otherwise returns true.
       
  2861 
       
  2862     \sa QMailCodec
       
  2863 */
       
  2864 bool QMailMessageBody::toStream(QDataStream& out, EncodingFormat format) const
       
  2865 {
       
  2866     return impl(this)->toStream(out, format);
       
  2867 }
       
  2868 
       
  2869 /*!
       
  2870     Returns the data of the message body as a QString, in unencoded form.  Line-endings
       
  2871     transmitted as CR/LF pairs are converted to \c \n on extraction.
       
  2872 
       
  2873     The 'charset' parameter of the body's content type is used to locate the appropriate 
       
  2874     QTextCodec to convert the data from an octet stream to unicode, if necessary.
       
  2875 
       
  2876     \sa QMailCodec, QMailMessageContentType, QTextCodec
       
  2877 */
       
  2878 QString QMailMessageBody::data() const
       
  2879 {
       
  2880     QString result;
       
  2881     {
       
  2882         QTextStream out(&result, QIODevice::WriteOnly);
       
  2883         impl(this)->toStream(out);
       
  2884     }
       
  2885     return result;
       
  2886 }
       
  2887 
       
  2888 /*!
       
  2889     Writes the data of the message body to the stream \a out, in unencoded form. 
       
  2890     Line-endings transmitted as CR/LF pairs are converted to \c \n on extraction.
       
  2891     Returns false if the operation causes an error; otherwise returns true.
       
  2892 
       
  2893     The 'charset' parameter of the body's content type is used to locate the appropriate 
       
  2894     QTextCodec to convert the data from an octet stream to unicode, if necessary.
       
  2895 
       
  2896     \sa QMailCodec, QMailMessageContentType, QTextCodec
       
  2897 */
       
  2898 bool QMailMessageBody::toStream(QTextStream& out) const
       
  2899 {
       
  2900     return impl(this)->toStream(out);
       
  2901 }
       
  2902 
       
  2903 /*!
       
  2904     Returns the content type that the body was created with.
       
  2905 */
       
  2906 QMailMessageContentType QMailMessageBody::contentType() const
       
  2907 {
       
  2908     return impl(this)->contentType();
       
  2909 }
       
  2910 
       
  2911 /*!
       
  2912     Returns the transfer encoding type that the body was created with.
       
  2913 */
       
  2914 QMailMessageBody::TransferEncoding QMailMessageBody::transferEncoding() const
       
  2915 {
       
  2916     return impl(this)->transferEncoding();
       
  2917 }
       
  2918 
       
  2919 /*!
       
  2920     Returns true if the body does not contain any data.
       
  2921 */
       
  2922 bool QMailMessageBody::isEmpty() const
       
  2923 {
       
  2924     return impl(this)->isEmpty();
       
  2925 }
       
  2926 
       
  2927 /*!
       
  2928     Returns the length of the body data in bytes.
       
  2929 */
       
  2930 int QMailMessageBody::length() const
       
  2931 {
       
  2932     return impl(this)->length();
       
  2933 }
       
  2934 
       
  2935 /*! \internal */
       
  2936 uint QMailMessageBody::indicativeSize() const
       
  2937 {
       
  2938     return impl(this)->indicativeSize();
       
  2939 }
       
  2940 
       
  2941 /*! \internal */
       
  2942 void QMailMessageBody::output(QDataStream& out, bool includeAttachments) const
       
  2943 {
       
  2944     impl(this)->output(out, includeAttachments);
       
  2945 }
       
  2946 
       
  2947 /*! 
       
  2948     \fn QMailMessageBody::serialize(Stream&) const
       
  2949     \internal 
       
  2950 */
       
  2951 template <typename Stream> 
       
  2952 void QMailMessageBody::serialize(Stream &stream) const
       
  2953 {
       
  2954     impl(this)->serialize(stream);
       
  2955 }
       
  2956 
       
  2957 /*! 
       
  2958     \fn QMailMessageBody::deserialize(Stream&)
       
  2959     \internal 
       
  2960 */
       
  2961 template <typename Stream> 
       
  2962 void QMailMessageBody::deserialize(Stream &stream)
       
  2963 {
       
  2964     impl(this)->deserialize(stream);
       
  2965 }
       
  2966 
       
  2967 
       
  2968 class QMailMessagePart::LocationPrivate
       
  2969 {
       
  2970 public:
       
  2971     QMailMessageId _messageId;
       
  2972     QList<uint> _indices;
       
  2973 };
       
  2974 
       
  2975 
       
  2976 /* QMailMessagePartContainer */
       
  2977 
       
  2978 template<typename Derived>
       
  2979 QMailMessagePartContainerPrivate::QMailMessagePartContainerPrivate(Derived* p)
       
  2980     : QPrivateImplementationBase(p)
       
  2981 {
       
  2982     _multipartType = QMailMessagePartContainer::MultipartNone;
       
  2983     _hasBody = false;
       
  2984     _dirty = false;
       
  2985 }
       
  2986 
       
  2987 void QMailMessagePartContainerPrivate::setLocation(const QMailMessageId& id, const QList<uint>& indices)
       
  2988 {
       
  2989     _messageId = id;
       
  2990     _indices = indices;
       
  2991 
       
  2992     if (!_messageParts.isEmpty()) {
       
  2993         QList<QMailMessagePart>::iterator it = _messageParts.begin(), end = _messageParts.end();
       
  2994         for (uint i = 0; it != end; ++it, ++i) {
       
  2995             QList<uint> location(_indices);
       
  2996             location.append(i + 1);
       
  2997             (*it).impl<QMailMessagePartContainerPrivate>()->setLocation(_messageId, location);
       
  2998         }
       
  2999     }
       
  3000 }
       
  3001 
       
  3002 int QMailMessagePartContainerPrivate::partNumber() const
       
  3003 {
       
  3004     return (_indices.last() - 1);
       
  3005 }
       
  3006 
       
  3007 bool QMailMessagePartContainerPrivate::contains(const QMailMessagePart::Location& location) const
       
  3008 {
       
  3009     const QMailMessagePart* part = 0; 
       
  3010     const QList<QMailMessagePart>* partList = &_messageParts; 
       
  3011 
       
  3012     foreach (uint index, location.d->_indices) {
       
  3013         if (partList->count() < index) {
       
  3014             return false;
       
  3015         }
       
  3016 
       
  3017         part = &(partList->at(index - 1));
       
  3018         partList = &(part->impl<const QMailMessagePartContainerPrivate>()->_messageParts);
       
  3019     }
       
  3020 
       
  3021     return true;
       
  3022 }
       
  3023 
       
  3024 const QMailMessagePart& QMailMessagePartContainerPrivate::partAt(const QMailMessagePart::Location& location) const
       
  3025 {
       
  3026     const QMailMessagePart* part = 0; 
       
  3027     const QList<QMailMessagePart>* partList = &_messageParts; 
       
  3028 
       
  3029     foreach (uint index, location.d->_indices) {
       
  3030         part = &(partList->at(index - 1));
       
  3031         partList = &(part->impl<const QMailMessagePartContainerPrivate>()->_messageParts);
       
  3032     }
       
  3033 
       
  3034     Q_ASSERT(part);
       
  3035     return *part;
       
  3036 }
       
  3037 
       
  3038 QMailMessagePart& QMailMessagePartContainerPrivate::partAt(const QMailMessagePart::Location& location)
       
  3039 {
       
  3040     QMailMessagePart* part = 0; 
       
  3041     QList<QMailMessagePart>* partList = &_messageParts; 
       
  3042 
       
  3043     foreach (uint index, location.d->_indices) {
       
  3044         part = &((*partList)[index - 1]);
       
  3045         partList = &(part->impl<QMailMessagePartContainerPrivate>()->_messageParts);
       
  3046     }
       
  3047 
       
  3048     return *part;
       
  3049 }
       
  3050 
       
  3051 void QMailMessagePartContainerPrivate::setHeader(const QMailMessageHeader& partHeader, const QMailMessagePartContainerPrivate* parent)
       
  3052 {
       
  3053     _header = partHeader;
       
  3054 
       
  3055     defaultContentType(parent);
       
  3056 
       
  3057     QByteArray contentType = headerField("Content-Type");
       
  3058     if (!contentType.isEmpty())
       
  3059     {
       
  3060         // Extract the stored parts from the supplied field
       
  3061         QMailMessageContentType type(contentType);
       
  3062         _multipartType = QMailMessagePartContainer::multipartTypeForName(type.content());
       
  3063         _boundary = type.boundary();
       
  3064     }
       
  3065 }
       
  3066 
       
  3067 void QMailMessagePartContainerPrivate::defaultContentType(const QMailMessagePartContainerPrivate* parent)
       
  3068 {
       
  3069     QMailMessageContentType type;
       
  3070 
       
  3071     // Find the content-type, or use default values
       
  3072     QByteArray contentType = headerField("Content-Type");
       
  3073     bool useDefault = contentType.isEmpty();
       
  3074 
       
  3075     if (!useDefault)
       
  3076     {
       
  3077         type = QMailMessageContentType(contentType);
       
  3078 
       
  3079         if (type.type().isEmpty() || type.subType().isEmpty())
       
  3080         {
       
  3081             useDefault = true;
       
  3082         }
       
  3083         else if (insensitiveEqual(type.content(), "application/octet-stream"))
       
  3084         {
       
  3085             // Sender's client might not know what type, but maybe we do. Try...
       
  3086             QByteArray contentDisposition = headerField("Content-Disposition");
       
  3087             if (!contentDisposition.isEmpty())
       
  3088             {
       
  3089                 QMailMessageContentDisposition disposition(contentDisposition);
       
  3090 
       
  3091                 QString mimeType = QMail::mimeTypeFromFileName(disposition.filename());
       
  3092                 if (!mimeType.isEmpty())
       
  3093                 {
       
  3094                     type.setContent(to7BitAscii(mimeType));
       
  3095                     updateHeaderField(type.id(), type.toString(false, false));
       
  3096                 }
       
  3097             }
       
  3098         }
       
  3099     }
       
  3100 
       
  3101     if (useDefault && parent)
       
  3102     {
       
  3103         // Note that the default is 'message/rfc822' when the parent is 'multipart/digest'
       
  3104         QMailMessageContentType parentType = parent->contentType();
       
  3105         if (parentType.content().toLower() == "multipart/digest")
       
  3106         {
       
  3107             type.setType("message");
       
  3108             type.setSubType("rfc822");
       
  3109             updateHeaderField(type.id(), type.toString(false, false));
       
  3110             useDefault = false;
       
  3111         }
       
  3112     }
       
  3113 
       
  3114     if (useDefault)
       
  3115     {
       
  3116         type.setType("text");
       
  3117         type.setSubType("plain");
       
  3118         type.setCharset("us-ascii");
       
  3119         updateHeaderField(type.id(), type.toString(false, false));
       
  3120     }
       
  3121 }
       
  3122 
       
  3123 /*! \internal */
       
  3124 uint QMailMessagePartContainerPrivate::indicativeSize() const
       
  3125 {
       
  3126     uint size = 0;
       
  3127 
       
  3128     if (hasBody()) {
       
  3129         size = body().indicativeSize();
       
  3130     } else {
       
  3131         for (int i = 0; i < _messageParts.count(); ++i)
       
  3132             size += _messageParts[i].indicativeSize();
       
  3133     }
       
  3134 
       
  3135     return size;
       
  3136 }
       
  3137 
       
  3138 template <typename F>
       
  3139 void QMailMessagePartContainerPrivate::outputParts(QDataStream **out, bool addMimePreamble, bool includeAttachments, bool excludeInternalFields, F *func) const
       
  3140 {
       
  3141     static const DataString newLine('\n');
       
  3142     static const DataString marker("--");
       
  3143 
       
  3144     if (_multipartType == QMailMessagePartContainer::MultipartNone)
       
  3145         return;
       
  3146 
       
  3147     if (addMimePreamble) {
       
  3148         // This is a preamble (not for conformance, to assist readibility on non-conforming renderers):
       
  3149         **out << DataString("This is a multipart message in Mime 1.0 format"); // No tr
       
  3150         **out << newLine;
       
  3151     }
       
  3152 
       
  3153     for ( int i = 0; i < _messageParts.count(); i++ ) {
       
  3154         **out << newLine << marker << DataString(_boundary) << newLine;
       
  3155 
       
  3156         QMailMessagePart& part = const_cast<QMailMessagePart&>(_messageParts[i]);
       
  3157 
       
  3158         if (part.multipartType() != QMailMessagePartContainer::MultipartNone) {
       
  3159             const QString &partBoundary(part.boundary());
       
  3160 
       
  3161             if (partBoundary.isEmpty()) {
       
  3162                 QString subBoundary(_boundary);
       
  3163                 int index = subBoundary.indexOf(':');
       
  3164                 if (index != -1) {
       
  3165                     subBoundary.insert(index, QString::number(part.partNumber()).prepend("-"));
       
  3166                 } else {
       
  3167                     // Shouldn't happen...
       
  3168                     subBoundary.insert(0, QString::number(part.partNumber()).append(":"));
       
  3169                 }
       
  3170 
       
  3171                 part.setBoundary(to7BitAscii(subBoundary));
       
  3172             }
       
  3173         }
       
  3174         QMailMessagePartPrivate *partImpl(part.impl<QMailMessagePartPrivate>());
       
  3175         partImpl->output<F>(out, false, includeAttachments, excludeInternalFields, func);
       
  3176     }
       
  3177 
       
  3178     **out << newLine << marker << DataString(_boundary) << marker << newLine;
       
  3179 }
       
  3180 
       
  3181 void QMailMessagePartContainerPrivate::outputBody(QDataStream& out, bool includeAttachments) const
       
  3182 {
       
  3183     _body.output(out, includeAttachments);
       
  3184 }
       
  3185 
       
  3186 static QString decodedContent(const QString& id, const QByteArray& content)
       
  3187 {
       
  3188     // TODO: Potentially, we should disallow decoding here based on the specific header field
       
  3189     bool permitDecoding(true);
       
  3190     //QByteArray id(fieldId(to7BitAscii(id)));
       
  3191     Q_UNUSED(id)
       
  3192 
       
  3193     return (permitDecoding ? QMailMessageHeaderField::decodeContent(content) : QString(content));
       
  3194 }
       
  3195 
       
  3196 /*!
       
  3197     Returns the text of the first header field with the given \a id.
       
  3198 */
       
  3199 QString QMailMessagePartContainerPrivate::headerFieldText( const QString &id ) const
       
  3200 {
       
  3201     const QByteArray& content = headerField( to7BitAscii(id) );
       
  3202     return decodedContent( id, content );
       
  3203 }
       
  3204 
       
  3205 static QMailMessageContentType updateContentType(const QByteArray& existing, QMailMessagePartContainer::MultipartType multipartType, const QByteArray& boundary)
       
  3206 {
       
  3207     // Ensure that any parameters of the existing field are preserved
       
  3208     QMailMessageContentType existingType(existing);
       
  3209     QList<QMailMessageHeaderField::ParameterType> parameters = existingType.parameters();
       
  3210 
       
  3211     QMailMessageContentType type(QMailMessagePartContainer::nameForMultipartType(multipartType));
       
  3212     foreach (const QMailMessageHeaderField::ParameterType& param, parameters)
       
  3213         type.setParameter(param.first, param.second);
       
  3214 
       
  3215     if (!boundary.isEmpty())
       
  3216         type.setBoundary(boundary);
       
  3217 
       
  3218     return type;
       
  3219 }
       
  3220 
       
  3221 void QMailMessagePartContainerPrivate::setMultipartType(QMailMessagePartContainer::MultipartType type)
       
  3222 {
       
  3223     // TODO: Is there any value in keeping _multipartType and _boundary externally from
       
  3224     // Content-type header field?
       
  3225 
       
  3226     if (_multipartType != type) {
       
  3227         _multipartType = type;
       
  3228         setDirty();
       
  3229 
       
  3230         if (_multipartType == QMailMessagePartContainer::MultipartNone) {
       
  3231             removeHeaderField("Content-Type");
       
  3232         } else  {
       
  3233             QMailMessageContentType contentType = updateContentType(headerField("Content-Type"), _multipartType, _boundary);
       
  3234             updateHeaderField("Content-Type", contentType.toString(false, false));
       
  3235 
       
  3236             if (_hasBody) {
       
  3237                 _body = QMailMessageBody();
       
  3238                 _hasBody = false;
       
  3239             }
       
  3240         }
       
  3241     }
       
  3242 }
       
  3243 
       
  3244 QByteArray QMailMessagePartContainerPrivate::boundary() const
       
  3245 {
       
  3246     return _boundary;
       
  3247 }
       
  3248 
       
  3249 void QMailMessagePartContainerPrivate::setBoundary(const QByteArray& text)
       
  3250 {
       
  3251     _boundary = text;
       
  3252 
       
  3253     if (_multipartType != QMailMessagePartContainer::MultipartNone) {
       
  3254         QMailMessageContentType type = updateContentType(headerField("Content-Type"), _multipartType, _boundary);
       
  3255         updateHeaderField("Content-Type", type.toString(false, false));
       
  3256     } else {
       
  3257         QMailMessageHeaderField type("Content-Type", headerField("Content-Type"));
       
  3258         type.setParameter("boundary", _boundary);
       
  3259         updateHeaderField("Content-Type", type.toString(false, false));
       
  3260     }
       
  3261 }
       
  3262 
       
  3263 QMailMessageBody& QMailMessagePartContainerPrivate::body()
       
  3264 {
       
  3265     return _body;
       
  3266 }
       
  3267 
       
  3268 const QMailMessageBody& QMailMessagePartContainerPrivate::body() const
       
  3269 {
       
  3270     return const_cast<QMailMessagePartContainerPrivate*>(this)->_body;
       
  3271 }
       
  3272 
       
  3273 void QMailMessagePartContainerPrivate::setBody(const QMailMessageBody& body)
       
  3274 {
       
  3275     // Set the body's properties into our header
       
  3276     setBodyProperties(body.contentType(), body.transferEncoding());
       
  3277 
       
  3278     // Multipart messages do not have their own bodies
       
  3279     if (body.contentType().type().toLower() != "multipart") {
       
  3280         _body = body;
       
  3281         _hasBody = !_body.isEmpty();
       
  3282     }
       
  3283 }
       
  3284 
       
  3285 void QMailMessagePartContainerPrivate::setBodyProperties(const QMailMessageContentType &type, QMailMessageBody::TransferEncoding encoding)
       
  3286 {
       
  3287     updateHeaderField(type.id(), type.toString(false, false));
       
  3288 
       
  3289     QByteArray encodingName(nameForEncoding(encoding));
       
  3290     if (!encodingName.isEmpty()) {
       
  3291         updateHeaderField("Content-Transfer-Encoding", encodingName);
       
  3292     }
       
  3293 
       
  3294     setDirty();
       
  3295 }
       
  3296 
       
  3297 bool QMailMessagePartContainerPrivate::hasBody() const
       
  3298 {
       
  3299     return _hasBody;
       
  3300 }
       
  3301 
       
  3302 static QByteArray plainId(const QByteArray &id)
       
  3303 {
       
  3304     QByteArray name(id.trimmed());
       
  3305     if (name.endsWith(':'))
       
  3306         name.chop(1);
       
  3307     return name.trimmed();
       
  3308 }
       
  3309 
       
  3310 QByteArray QMailMessagePartContainerPrivate::headerField( const QByteArray &name ) const
       
  3311 {
       
  3312     QList<QByteArray> result = headerFields(name, 1);
       
  3313     if (result.count())
       
  3314         return result[0];
       
  3315 
       
  3316     return QByteArray();
       
  3317 }
       
  3318 
       
  3319 QList<QByteArray> QMailMessagePartContainerPrivate::headerFields( const QByteArray &name, int maximum ) const
       
  3320 {
       
  3321     QList<QByteArray> result;
       
  3322 
       
  3323     QByteArray id(plainId(name));
       
  3324 
       
  3325     foreach (const QByteArray* field, _header.fieldList()) {
       
  3326         QMailMessageHeaderField headerField(*field, QMailMessageHeaderField::UnstructuredField);
       
  3327         if (insensitiveEqual(headerField.id(), id)) {
       
  3328             result.append(headerField.content());
       
  3329             if (maximum > 0 && result.count() == maximum)
       
  3330                 break;
       
  3331         }
       
  3332     }
       
  3333 
       
  3334     return result;
       
  3335 }
       
  3336 
       
  3337 QList<QByteArray> QMailMessagePartContainerPrivate::headerFields() const
       
  3338 {
       
  3339     QList<QByteArray> result;
       
  3340 
       
  3341     foreach (const QByteArray* field, _header.fieldList())
       
  3342         result.append(*field);
       
  3343 
       
  3344     return result;
       
  3345 }
       
  3346 
       
  3347 void QMailMessagePartContainerPrivate::updateHeaderField(const QByteArray &id, const QByteArray &content)
       
  3348 {
       
  3349     _header.update(id, content);
       
  3350     setDirty();
       
  3351 
       
  3352     if (insensitiveEqual(plainId(id), "Content-Type"))
       
  3353     {
       
  3354         // Extract the stored parts from the supplied field
       
  3355         QMailMessageContentType type(content);
       
  3356         _multipartType = QMailMessagePartContainer::multipartTypeForName(type.content());
       
  3357         _boundary = type.boundary();
       
  3358     }
       
  3359 }
       
  3360 
       
  3361 static QByteArray encodedContent(const QByteArray& id, const QString& content)
       
  3362 {
       
  3363     // TODO: Potentially, we should disallow encoding here based on the specific header field
       
  3364     bool permitEncoding(true);
       
  3365     //QByteArray name(fieldId(id));
       
  3366     Q_UNUSED(id)
       
  3367 
       
  3368     return (permitEncoding ? QMailMessageHeaderField::encodeContent(content) : to7BitAscii(content));
       
  3369 }
       
  3370 
       
  3371 void QMailMessagePartContainerPrivate::updateHeaderField(const QByteArray &id, const QString &content)
       
  3372 {
       
  3373     updateHeaderField(id, encodedContent(id, content));
       
  3374 }
       
  3375 
       
  3376 void QMailMessagePartContainerPrivate::appendHeaderField(const QByteArray &id, const QByteArray &content)
       
  3377 {
       
  3378     _header.append( id, content );
       
  3379     setDirty();
       
  3380 
       
  3381     if (insensitiveEqual(plainId(id), "Content-Type"))
       
  3382     {
       
  3383         // Extract the stored parts from the supplied field
       
  3384         QMailMessageContentType type(content);
       
  3385         _multipartType = QMailMessagePartContainer::multipartTypeForName(type.content());
       
  3386         _boundary = type.boundary();
       
  3387     }
       
  3388 }
       
  3389 
       
  3390 void QMailMessagePartContainerPrivate::appendHeaderField(const QByteArray &id, const QString &content)
       
  3391 {
       
  3392     appendHeaderField(id, encodedContent(id, content));
       
  3393 }
       
  3394 
       
  3395 void QMailMessagePartContainerPrivate::removeHeaderField(const QByteArray &id)
       
  3396 {
       
  3397     _header.remove(id);
       
  3398     setDirty();
       
  3399 
       
  3400     if (insensitiveEqual(plainId(id), "Content-Type"))
       
  3401     {
       
  3402         // Extract the stored parts from the supplied field
       
  3403         _multipartType = QMailMessagePartContainer::MultipartNone;
       
  3404         _boundary = QByteArray();
       
  3405     }
       
  3406 }
       
  3407 
       
  3408 void QMailMessagePartContainerPrivate::appendPart(const QMailMessagePart &part)
       
  3409 {
       
  3410     QList<QMailMessagePart>::iterator it = _messageParts.insert( _messageParts.end(), part );
       
  3411 
       
  3412     QList<uint> location(_indices);
       
  3413     location.append(_messageParts.count());
       
  3414     (*it).impl<QMailMessagePartContainerPrivate>()->setLocation(_messageId, location);
       
  3415 
       
  3416     setDirty();
       
  3417 }
       
  3418 
       
  3419 void QMailMessagePartContainerPrivate::prependPart(const QMailMessagePart &part)
       
  3420 {
       
  3421     // Increment the part numbers for existing parts
       
  3422     QList<QMailMessagePart>::iterator it = _messageParts.begin(), end = _messageParts.end();
       
  3423     for (uint i = 1; it != end; ++it, ++i) {
       
  3424         QList<uint> location(_indices);
       
  3425         location.append(i + 1);
       
  3426         (*it).impl<QMailMessagePartContainerPrivate>()->setLocation(_messageId, location);
       
  3427     }
       
  3428 
       
  3429     it = _messageParts.insert( _messageParts.begin(), part );
       
  3430 
       
  3431     QList<uint> location(_indices);
       
  3432     location.append(1);
       
  3433     (*it).impl<QMailMessagePartContainerPrivate>()->setLocation(_messageId, location);
       
  3434 
       
  3435     setDirty();
       
  3436 }
       
  3437 
       
  3438 void QMailMessagePartContainerPrivate::removePartAt(uint pos)
       
  3439 {
       
  3440     _messageParts.removeAt(pos);
       
  3441     setDirty();
       
  3442 }
       
  3443 
       
  3444 void QMailMessagePartContainerPrivate::clear()
       
  3445 {
       
  3446     if (!_messageParts.isEmpty()) {
       
  3447         _messageParts.clear();
       
  3448         setDirty();
       
  3449     }
       
  3450 }
       
  3451 
       
  3452 QMailMessageContentType QMailMessagePartContainerPrivate::contentType() const
       
  3453 {
       
  3454     return QMailMessageContentType(headerField("Content-Type"));
       
  3455 }
       
  3456 
       
  3457 QMailMessageBody::TransferEncoding QMailMessagePartContainerPrivate::transferEncoding() const
       
  3458 {
       
  3459     return encodingForName(headerField("Content-Transfer-Encoding"));
       
  3460 }
       
  3461 
       
  3462 void QMailMessagePartContainerPrivate::parseMimeSinglePart(const QMailMessageHeader& partHeader, LongString body)
       
  3463 {
       
  3464     // Create a part to contain this data
       
  3465     QMailMessagePart part;
       
  3466     part.setHeader(partHeader, this);
       
  3467 
       
  3468     QMailMessageContentType contentType(part.headerField("Content-Type"));
       
  3469     QMailMessageBody::TransferEncoding encoding = encodingForName(part.headerFieldText("Content-Transfer-Encoding").toLatin1());
       
  3470     if ( encoding == QMailMessageBody::NoEncoding )
       
  3471         encoding = QMailMessageBody::SevenBit;
       
  3472 
       
  3473     if ( contentType.type() == "message" ) { // No tr
       
  3474         // TODO: We can't currently handle these types
       
  3475     }
       
  3476 
       
  3477     part.setBody(QMailMessageBody::fromLongString(body, contentType, encoding, QMailMessageBody::AlreadyEncoded));
       
  3478 
       
  3479     appendPart(part);
       
  3480 }
       
  3481 
       
  3482 void QMailMessagePartContainerPrivate::parseMimeMultipart(const QMailMessageHeader& partHeader, LongString body, bool insertIntoSelf)
       
  3483 {
       
  3484     static const QByteArray newLine(QMailMessage::CRLF);
       
  3485     static const QByteArray marker("--");
       
  3486 
       
  3487     QMailMessagePart part;
       
  3488     QMailMessageContentType contentType;
       
  3489     QByteArray boundary;
       
  3490     QMailMessagePartContainerPrivate* multipartContainer = 0;
       
  3491 
       
  3492     if (insertIntoSelf) {
       
  3493         // Insert the parts into ourself
       
  3494         multipartContainer = this;
       
  3495         contentType = QMailMessageContentType(headerField("Content-Type"));
       
  3496         boundary = _boundary;
       
  3497     } else {
       
  3498         // This object already contains part(s) - use a new part to contain the parts
       
  3499         multipartContainer = privatePointer(part);
       
  3500 
       
  3501         // Parse the header fields, and update the part
       
  3502         part.setHeader(partHeader, this);
       
  3503         contentType = QMailMessageContentType(part.headerField("Content-Type"));
       
  3504         boundary = contentType.boundary();
       
  3505     }
       
  3506 
       
  3507     // Separate the body into parts delimited by the boundary, and parse them individually
       
  3508     QByteArray partDelimiter = marker + boundary;
       
  3509     QByteArray partTerminator = newLine + partDelimiter + marker;
       
  3510 
       
  3511     int startPos = body.indexOf(partDelimiter, 0);
       
  3512     if (startPos != -1)
       
  3513         startPos += partDelimiter.length();
       
  3514 
       
  3515     // Subsequent delimiters include the leading newline
       
  3516     partDelimiter.prepend(newLine);
       
  3517 
       
  3518     int endPos = body.indexOf(partTerminator, 0);
       
  3519     while ((startPos != -1) && (startPos < endPos))
       
  3520     {
       
  3521         // Skip the boundary line
       
  3522         startPos = body.indexOf(newLine, startPos);
       
  3523 
       
  3524         if ((startPos != -1) && (startPos < endPos))
       
  3525         {
       
  3526             // Parse the section up to the next boundary marker
       
  3527             int nextPos = body.indexOf(partDelimiter, startPos);
       
  3528             multipartContainer->parseMimePart(body.mid(startPos, (nextPos - startPos)));
       
  3529 
       
  3530             // Try the next part
       
  3531             startPos = nextPos + partDelimiter.length();
       
  3532         }
       
  3533     }
       
  3534 
       
  3535     if (part.partCount() > 0) {
       
  3536         appendPart(part);
       
  3537     }
       
  3538 }
       
  3539 
       
  3540 bool QMailMessagePartContainerPrivate::parseMimePart(LongString body)
       
  3541 {
       
  3542     static const QByteArray delimiter((QByteArray(QMailMessage::CRLF) + QMailMessage::CRLF));
       
  3543 
       
  3544     int startPos = 0;
       
  3545     int endPos = body.indexOf(delimiter);
       
  3546 
       
  3547     if (startPos <= endPos) {
       
  3548         // startPos is the offset of the header, endPos of the delimiter preceding the body
       
  3549         QByteArray header(body.mid(startPos, endPos - startPos).toQByteArray());
       
  3550 
       
  3551         // Bypass the delimiter
       
  3552         LongString remainder = body.mid(endPos + delimiter.length());
       
  3553 
       
  3554         QMailMessageHeader partHeader = QMailMessageHeader(header);
       
  3555         QMailMessageContentType contentType(partHeader.field("Content-Type"));
       
  3556 
       
  3557         // If the content is not available, treat the part as simple
       
  3558         if (insensitiveEqual(contentType.type(), "multipart") && !remainder.isEmpty()) {
       
  3559             // Parse the body as a multi-part
       
  3560             parseMimeMultipart(partHeader, remainder, false);
       
  3561         } else {
       
  3562             // Parse the remainder as a single part
       
  3563             parseMimeSinglePart(partHeader, remainder);
       
  3564         }
       
  3565         return true;
       
  3566     }
       
  3567 
       
  3568     return false;
       
  3569 }
       
  3570 
       
  3571 bool QMailMessagePartContainerPrivate::dirty(bool recursive) const
       
  3572 {
       
  3573     if (_dirty)
       
  3574         return true;
       
  3575 
       
  3576     if (recursive) {
       
  3577         foreach (const QMailMessagePart& part, _messageParts)
       
  3578             if (part.impl<const QMailMessagePartContainerPrivate>()->dirty(true))
       
  3579                 return true;
       
  3580     }
       
  3581 
       
  3582     return false;
       
  3583 }
       
  3584 
       
  3585 void QMailMessagePartContainerPrivate::setDirty(bool value, bool recursive)
       
  3586 {
       
  3587     _dirty = value;
       
  3588 
       
  3589     if (recursive) {
       
  3590         const QList<QMailMessagePart>::Iterator end = _messageParts.end();
       
  3591         for (QList<QMailMessagePart>::Iterator it = _messageParts.begin(); it != end; ++it)
       
  3592             (*it).impl<QMailMessagePartContainerPrivate>()->setDirty(value, true);
       
  3593     }
       
  3594 }
       
  3595 
       
  3596 template <typename Stream> 
       
  3597 void QMailMessagePartContainerPrivate::serialize(Stream &stream) const
       
  3598 {
       
  3599     stream << _multipartType;
       
  3600     stream << _messageParts;
       
  3601     stream << _boundary;
       
  3602     stream << _header;
       
  3603     stream << _messageId;
       
  3604     stream << _indices;
       
  3605     stream << _hasBody;
       
  3606     if (_hasBody)
       
  3607         stream << _body;
       
  3608     stream << _dirty;
       
  3609 }
       
  3610 
       
  3611 template <typename Stream> 
       
  3612 void QMailMessagePartContainerPrivate::deserialize(Stream &stream)
       
  3613 {
       
  3614     stream >> _multipartType;
       
  3615     stream >> _messageParts;
       
  3616     stream >> _boundary;
       
  3617     stream >> _header;
       
  3618     stream >> _messageId;
       
  3619     stream >> _indices;
       
  3620     stream >> _hasBody;
       
  3621     if (_hasBody)
       
  3622         stream >> _body;
       
  3623     stream >> _dirty;
       
  3624 }
       
  3625 
       
  3626 
       
  3627 /*!
       
  3628     \class QMailMessagePartContainer
       
  3629 
       
  3630     \brief The QMailMessagePartContainer class provides access to a collection of message parts.
       
  3631     
       
  3632     \ingroup messaginglibrary
       
  3633    
       
  3634     Message formats such as email messages conforming to 
       
  3635     \l{http://www.ietf.org/rfc/rfc2822.txt} {RFC 2822} (Internet Message Format) can consist of 
       
  3636     multiple independent parts, whose relationship to each other is defined by the message that 
       
  3637     contains those parts.  The QMailMessagePartContainer class provides storage for these related 
       
  3638     message parts, and the interface through which they are accessed.
       
  3639 
       
  3640     The multipartType() function returns a member of the MultipartType enumeration, which 
       
  3641     describes the relationship of the parts in the container to each other.
       
  3642 
       
  3643     The part container can instead contain a message body element.  In this case, it cannot contain
       
  3644     sub-parts, and the multipartType() function will return MultipartType::MultipartNone for the part. 
       
  3645     The body element can be accessed via the body() function.
       
  3646 
       
  3647     The QMailMessagePart class is itself derived from QMailMessagePartContainer, which allows
       
  3648     messages to support the nesting of part collections within other part collections.
       
  3649 
       
  3650     \sa QMailMessagePart, QMailMessage, QMailMessageBody
       
  3651 */    
       
  3652 
       
  3653 /*!
       
  3654     \typedef QMailMessagePartContainer::ImplementationType
       
  3655     \internal
       
  3656 */
       
  3657 
       
  3658 /*!
       
  3659     \fn QMailMessagePartContainer::QMailMessagePartContainer(Subclass*)
       
  3660 
       
  3661     Constructs an empty part container object, in the space allocated 
       
  3662     within the subclass instance at \a p.
       
  3663 */
       
  3664 template<typename Subclass>
       
  3665 QMailMessagePartContainer::QMailMessagePartContainer(Subclass* p)
       
  3666     : QPrivatelyImplemented<QMailMessagePartContainerPrivate>(p)
       
  3667 {
       
  3668 }
       
  3669 
       
  3670 /*! \internal */
       
  3671 void QMailMessagePartContainer::setHeader(const QMailMessageHeader& partHeader, const QMailMessagePartContainerPrivate* parent)
       
  3672 {
       
  3673     impl(this)->setHeader(partHeader, parent);
       
  3674 }
       
  3675 
       
  3676 /*!
       
  3677     Returns the number of attachments the message has.
       
  3678 */  
       
  3679 uint QMailMessagePartContainer::partCount() const
       
  3680 {
       
  3681     return impl(this)->_messageParts.count();
       
  3682 }
       
  3683 
       
  3684 /*!
       
  3685     Append \a part to the list of attachments for the message.
       
  3686 */
       
  3687 void QMailMessagePartContainer::appendPart(const QMailMessagePart &part)
       
  3688 {
       
  3689     impl(this)->appendPart(part);
       
  3690 }
       
  3691 
       
  3692 /*!
       
  3693     Prepend \a part to the list of attachments for the message.
       
  3694 */
       
  3695 void QMailMessagePartContainer::prependPart(const QMailMessagePart &part)
       
  3696 {
       
  3697     impl(this)->prependPart(part);
       
  3698 }
       
  3699 
       
  3700 /*!
       
  3701     Removes the part at the index \a pos.
       
  3702 
       
  3703     \a pos must be a valid index position in the list (i.e., 0 <= i < partCount()).
       
  3704 */
       
  3705 void QMailMessagePartContainer::removePartAt(uint pos)
       
  3706 {
       
  3707     impl(this)->removePartAt(pos);
       
  3708 }
       
  3709 
       
  3710 /*!
       
  3711     Returns a const reference to the item at position \a pos in the list of 
       
  3712     attachments for the message.
       
  3713 
       
  3714     \a pos must be a valid index position in the list (i.e., 0 <= i < partCount()).
       
  3715 */
       
  3716 const QMailMessagePart& QMailMessagePartContainer::partAt(uint pos) const
       
  3717 {
       
  3718     return impl(this)->_messageParts[pos];
       
  3719 }
       
  3720 
       
  3721 /*!
       
  3722     Returns a non-const reference to the item at position \a pos in the list of 
       
  3723     attachments for the message.
       
  3724 
       
  3725     \a pos must be a valid index position in the list (i.e., 0 <= i < partCount()).
       
  3726 */
       
  3727 QMailMessagePart& QMailMessagePartContainer::partAt(uint pos)
       
  3728 {
       
  3729     return impl(this)->_messageParts[pos];
       
  3730 }
       
  3731 
       
  3732 /*!
       
  3733     Clears the list of attachments associated with the message.
       
  3734 */
       
  3735 void QMailMessagePartContainer::clearParts()
       
  3736 {
       
  3737     impl(this)->clear();
       
  3738 }
       
  3739 
       
  3740 /*!
       
  3741     Returns the type of multipart relationship shared by the parts contained within this container, or
       
  3742     \l {QMailMessagePartContainerFwd::MultipartNone}{MultipartNone} if the content is not a multipart message.
       
  3743 */  
       
  3744 QMailMessagePartContainer::MultipartType QMailMessagePartContainer::multipartType() const
       
  3745 {
       
  3746     return impl(this)->_multipartType;
       
  3747 }
       
  3748 
       
  3749 /*!
       
  3750     Sets the multipart state of the message to \a type.
       
  3751 */
       
  3752 void QMailMessagePartContainer::setMultipartType(QMailMessagePartContainer::MultipartType type)
       
  3753 {
       
  3754     impl(this)->setMultipartType(type);
       
  3755 }
       
  3756 
       
  3757 /*!
       
  3758     Returns the boundary text used to delimit the container's parts when encoded in RFC 2822 form.
       
  3759 */
       
  3760 QByteArray QMailMessagePartContainer::boundary() const
       
  3761 {
       
  3762     return impl(this)->boundary();
       
  3763 }
       
  3764 
       
  3765 /*!
       
  3766     Sets the boundary text used to delimit the container's parts when encoded in RFC 2822 form to \a text.
       
  3767 */
       
  3768 void QMailMessagePartContainer::setBoundary(const QByteArray& text)
       
  3769 {
       
  3770     impl(this)->setBoundary(text);
       
  3771 }
       
  3772 
       
  3773 /*!
       
  3774     Sets the part to contain the body element \a body.
       
  3775 */
       
  3776 void QMailMessagePartContainer::setBody(const QMailMessageBody& body)
       
  3777 {
       
  3778     impl(this)->setBody(body);
       
  3779 }
       
  3780 
       
  3781 /*!
       
  3782     Returns the body element contained by the part.
       
  3783 */
       
  3784 QMailMessageBody QMailMessagePartContainer::body() const
       
  3785 {
       
  3786     return impl(this)->body();
       
  3787 }
       
  3788 
       
  3789 /*!
       
  3790     Returns true if the part contains a body element; otherwise returns false.
       
  3791 */
       
  3792 bool QMailMessagePartContainer::hasBody() const
       
  3793 {
       
  3794     return impl(this)->hasBody();
       
  3795 }
       
  3796 
       
  3797 /*!
       
  3798     Returns the content type of this part.  Where hasBody() is true, the type of the
       
  3799     contained body element is returned; otherwise a content type matching the 
       
  3800     multipartType() for this part is returned.
       
  3801 
       
  3802     \sa hasBody(), QMailMessageBody::contentType(), multipartType()
       
  3803 */
       
  3804 QMailMessageContentType QMailMessagePartContainer::contentType() const
       
  3805 {
       
  3806     return impl(this)->contentType();
       
  3807 }
       
  3808 
       
  3809 /*!
       
  3810     Returns the transfer encoding type of this part.  Where hasBody() is true, the
       
  3811     transfer encoding type of the contained body element is returned; otherwise, the
       
  3812     transfer encoding type specified by the 'Content-Transfer-Encoding' field of the 
       
  3813     header for this part is returned.
       
  3814 
       
  3815     \sa hasBody(), QMailMessageBody::transferEncoding()
       
  3816 */
       
  3817 QMailMessageBody::TransferEncoding QMailMessagePartContainer::transferEncoding() const
       
  3818 {
       
  3819     return impl(this)->transferEncoding();
       
  3820 }
       
  3821 
       
  3822 /*!
       
  3823     Returns the text of the first header field with the given \a id.
       
  3824 */
       
  3825 QString QMailMessagePartContainer::headerFieldText( const QString &id ) const
       
  3826 {
       
  3827     return impl(this)->headerFieldText(id);
       
  3828 }
       
  3829 
       
  3830 /*!
       
  3831     Returns an object containing the value of the first header field with the given \a id.
       
  3832     If \a fieldType is QMailMessageHeaderField::StructuredField, then the field content 
       
  3833     will be parsed assuming a format equivalent to that used for the RFC 2045 'Content-Type' 
       
  3834     and RFC 2183 'Content-Disposition' header fields.
       
  3835 */
       
  3836 QMailMessageHeaderField QMailMessagePartContainer::headerField( const QString &id, QMailMessageHeaderField::FieldType fieldType ) const
       
  3837 {
       
  3838     QByteArray plainId( to7BitAscii(id) );
       
  3839     const QByteArray& content = impl(this)->headerField( plainId );
       
  3840     if ( !content.isEmpty() )
       
  3841         return QMailMessageHeaderField( plainId, content, fieldType );
       
  3842 
       
  3843     return QMailMessageHeaderField();
       
  3844 }
       
  3845 
       
  3846 /*!
       
  3847     Returns a list containing the text of each header field with the given \a id.
       
  3848 */
       
  3849 QStringList QMailMessagePartContainer::headerFieldsText( const QString &id ) const
       
  3850 {
       
  3851     QStringList result;
       
  3852 
       
  3853     foreach (const QByteArray& item, impl(this)->headerFields( to7BitAscii(id) ))
       
  3854         result.append(decodedContent( id, item ));
       
  3855 
       
  3856     return result;
       
  3857 }
       
  3858 
       
  3859 /*!
       
  3860     Returns a list of objects containing the value of each header field with the given \a id.
       
  3861     If \a fieldType is QMailMessageHeaderField::StructuredField, then the field content will 
       
  3862     be parsed assuming a format equivalent to that used for the RFC 2045 'Content-Type' and 
       
  3863     RFC 2183 'Content-Disposition' header fields.
       
  3864 */
       
  3865 QList<QMailMessageHeaderField> QMailMessagePartContainer::headerFields( const QString &id, QMailMessageHeaderField::FieldType fieldType ) const
       
  3866 {
       
  3867     QList<QMailMessageHeaderField> result;
       
  3868 
       
  3869     QByteArray plainId( to7BitAscii(id) );
       
  3870     foreach (const QByteArray& content, impl(this)->headerFields( plainId ))
       
  3871         result.append( QMailMessageHeaderField( plainId, content, fieldType ) );
       
  3872 
       
  3873     return result;
       
  3874 }
       
  3875 
       
  3876 /*!
       
  3877     Returns a list of objects containing the value of each header field contained by the part.
       
  3878     Header field objects returned by this function are not 'structured'.
       
  3879 */
       
  3880 QList<QMailMessageHeaderField> QMailMessagePartContainer::headerFields() const
       
  3881 {
       
  3882     QList<QMailMessageHeaderField> result;
       
  3883 
       
  3884     foreach (const QByteArray& content, impl(this)->headerFields())
       
  3885         result.append( QMailMessageHeaderField( content, QMailMessageHeaderField::UnstructuredField) );
       
  3886 
       
  3887     return result;
       
  3888 }
       
  3889 
       
  3890 /*!
       
  3891     Sets the value of the first header field with identity \a id to \a value if it already 
       
  3892     exists; otherwise adds the header with the supplied id and value.  If \a value is of 
       
  3893     the form "<id>:<content>", then only the part after the semi-colon is processed.
       
  3894 
       
  3895     RFC 2822 encoding requires header fields to be transmitted in ASCII characters.  
       
  3896     If \a value contains non-ASCII characters, it will be encoded to ASCII via the 
       
  3897     QMailMessageHeaderField::encodeContent() function; depending on the specific header 
       
  3898     field this may result in illegal content.  Where possible, clients should encode 
       
  3899     non-ASCII data prior to calling setHeaderField.
       
  3900 
       
  3901     \sa QMailMessageHeaderField
       
  3902 */
       
  3903 void QMailMessagePartContainer::setHeaderField( const QString& id, const QString& value )
       
  3904 {
       
  3905     QByteArray plainId( to7BitAscii(id) );
       
  3906 
       
  3907     int index = value.indexOf(':');
       
  3908     if (index != -1 ) {
       
  3909         // Is the header field id replicated in the value?
       
  3910         QString prefix(value.left(index));
       
  3911         if ( insensitiveEqual( to7BitAscii(prefix.trimmed()), plainId.trimmed() ) ) {
       
  3912             impl(this)->updateHeaderField( plainId, value.mid(index + 1) );
       
  3913             return;
       
  3914         }
       
  3915     }
       
  3916 
       
  3917     impl(this)->updateHeaderField( plainId, value );
       
  3918 }
       
  3919 
       
  3920 /*!
       
  3921     Sets the first header field with identity matching \a field to have the content of
       
  3922     \a field.
       
  3923 */
       
  3924 void QMailMessagePartContainer::setHeaderField( const QMailMessageHeaderField& field )
       
  3925 {
       
  3926     impl(this)->updateHeaderField( field.id(), field.toString(false, false) );
       
  3927 }
       
  3928 
       
  3929 /*!
       
  3930     Appends a new header field with id \a id and value \a value to the existing
       
  3931     list of header fields.  Any existing header fields with the same id are not modified.
       
  3932     If \a value is of the form "<id>:<content>", then only the part after the 
       
  3933     semi-colon is processed.
       
  3934 
       
  3935     RFC 2822 encoding requires header fields to be transmitted in ASCII characters.  
       
  3936     If \a value contains non-ASCII characters, it will be encoded to ASCII via the 
       
  3937     QMailMessageHeaderField::encodeContent() function; depending on the specific header 
       
  3938     field this may result in illegal content.  Where possible, clients should encode 
       
  3939     non-ASCII data prior to calling appendHeaderField.
       
  3940 
       
  3941     \sa QMailMessageHeaderField
       
  3942 */
       
  3943 void QMailMessagePartContainer::appendHeaderField( const QString& id, const QString& value )
       
  3944 {
       
  3945     QByteArray plainId( to7BitAscii(id) );
       
  3946 
       
  3947     int index = value.indexOf(':');
       
  3948     if (index != -1 ) {
       
  3949         // Is the header field id replicated in the value?
       
  3950         QString prefix(value.left(index));
       
  3951         if ( insensitiveEqual( to7BitAscii(prefix.trimmed()), plainId.trimmed() ) ) {
       
  3952             impl(this)->appendHeaderField( plainId, value.mid(index + 1) );
       
  3953             return;
       
  3954         }
       
  3955     }
       
  3956 
       
  3957     impl(this)->appendHeaderField( plainId, value );
       
  3958 }
       
  3959 
       
  3960 /*!
       
  3961     Appends a new header field with the properties of \a field.  Any existing header 
       
  3962     fields with the same id are not modified.
       
  3963 */
       
  3964 void QMailMessagePartContainer::appendHeaderField( const QMailMessageHeaderField& field )
       
  3965 {
       
  3966     impl(this)->appendHeaderField( field.id(), field.toString(false, false) );
       
  3967 }
       
  3968 
       
  3969 /*!
       
  3970     Removes all existing header fields with id equal to \a id.
       
  3971 */
       
  3972 void QMailMessagePartContainer::removeHeaderField( const QString& id )
       
  3973 {
       
  3974     impl(this)->removeHeaderField( to7BitAscii(id) );
       
  3975 }
       
  3976 
       
  3977 /*!
       
  3978     Returns the multipart type that corresponds to the type name \a name.
       
  3979 */
       
  3980 QMailMessagePartContainer::MultipartType QMailMessagePartContainer::multipartTypeForName(const QByteArray &name)
       
  3981 {
       
  3982     QByteArray ciName = name.toLower();
       
  3983 
       
  3984     if ((ciName == "multipart/signed") || (ciName == "signed"))
       
  3985         return QMailMessagePartContainer::MultipartSigned;
       
  3986 
       
  3987     if ((ciName == "multipart/encrypted") || (ciName == "encrypted"))
       
  3988         return QMailMessagePartContainer::MultipartEncrypted;
       
  3989 
       
  3990     if ((ciName == "multipart/mixed") || (ciName == "mixed"))
       
  3991         return QMailMessagePartContainer::MultipartMixed;
       
  3992 
       
  3993     if ((ciName == "multipart/alternative") || (ciName == "alternative"))
       
  3994         return QMailMessagePartContainer::MultipartAlternative;
       
  3995 
       
  3996     if ((ciName == "multipart/digest") || (ciName == "digest"))
       
  3997         return QMailMessagePartContainer::MultipartDigest;
       
  3998 
       
  3999     if ((ciName == "multipart/parallel") || (ciName == "parallel"))
       
  4000         return QMailMessagePartContainer::MultipartParallel;
       
  4001 
       
  4002     if ((ciName == "multipart/related") || (ciName == "related"))
       
  4003         return QMailMessagePartContainer::MultipartRelated;
       
  4004 
       
  4005     if ((ciName == "multipart/form") || (ciName == "form"))
       
  4006         return QMailMessagePartContainer::MultipartFormData;
       
  4007 
       
  4008     if ((ciName == "multipart/report") || (ciName == "report"))
       
  4009         return QMailMessagePartContainer::MultipartReport;
       
  4010 
       
  4011     return QMailMessagePartContainer::MultipartNone;
       
  4012 }
       
  4013 
       
  4014 /*!
       
  4015     Returns the standard textual representation for the multipart type \a type.
       
  4016 */
       
  4017 QByteArray QMailMessagePartContainer::nameForMultipartType(QMailMessagePartContainer::MultipartType type)
       
  4018 {
       
  4019     switch (type) 
       
  4020     {
       
  4021         case QMailMessagePartContainer::MultipartSigned:
       
  4022         {
       
  4023             return "multipart/signed";
       
  4024         }
       
  4025         case QMailMessagePartContainer::MultipartEncrypted:
       
  4026         {
       
  4027             return "multipart/encrypted";
       
  4028         }
       
  4029         case QMailMessagePartContainer::MultipartMixed:
       
  4030         {
       
  4031             return "multipart/mixed";
       
  4032         }
       
  4033         case QMailMessagePartContainer::MultipartAlternative:
       
  4034         {
       
  4035             return "multipart/alternative";
       
  4036         }
       
  4037         case QMailMessagePartContainer::MultipartDigest:
       
  4038         {
       
  4039             return "multipart/digest";
       
  4040         }
       
  4041         case QMailMessagePartContainer::MultipartParallel:
       
  4042         {
       
  4043             return "multipart/parallel";
       
  4044         }
       
  4045         case QMailMessagePartContainer::MultipartRelated:
       
  4046         {
       
  4047             return "multipart/related";
       
  4048         }
       
  4049         case QMailMessagePartContainer::MultipartFormData:
       
  4050         {
       
  4051             return "multipart/form-data";
       
  4052         }
       
  4053         case QMailMessagePartContainer::MultipartReport:
       
  4054         {
       
  4055             return "multipart/report";
       
  4056         }
       
  4057         case QMailMessagePartContainer::MultipartNone:
       
  4058             break;
       
  4059     }
       
  4060 
       
  4061     return QByteArray();
       
  4062 }
       
  4063 
       
  4064 /*! \internal */
       
  4065 uint QMailMessagePartContainer::indicativeSize() const
       
  4066 {
       
  4067     return impl(this)->indicativeSize();
       
  4068 }
       
  4069 
       
  4070 struct DummyChunkProcessor
       
  4071 {
       
  4072     void operator()(QMailMessage::ChunkType) {}
       
  4073 };
       
  4074 
       
  4075 /*! \internal */
       
  4076 void QMailMessagePartContainer::outputParts(QDataStream& out, bool addMimePreamble, bool includeAttachments, bool excludeInternalFields) const
       
  4077 {
       
  4078     QDataStream* ds(&out);
       
  4079     impl(this)->outputParts<DummyChunkProcessor>(&ds, addMimePreamble, includeAttachments, excludeInternalFields, 0);
       
  4080 }
       
  4081 
       
  4082 /*! \internal */
       
  4083 void QMailMessagePartContainer::outputBody( QDataStream& out, bool includeAttachments ) const
       
  4084 {
       
  4085     impl(this)->outputBody( out, includeAttachments );
       
  4086 }
       
  4087 
       
  4088 /*!
       
  4089     \fn QMailMessagePartContainer::contentAvailable() const
       
  4090 
       
  4091     Returns true if the entire content of this element is available; otherwise returns false.
       
  4092 */
       
  4093 
       
  4094 /*!
       
  4095     \fn QMailMessagePartContainer::partialContentAvailable() const
       
  4096 
       
  4097     Returns true if some portion of the content of this element is available; otherwise returns false.
       
  4098 */
       
  4099 
       
  4100 
       
  4101 /* QMailMessagePart */
       
  4102 
       
  4103 QMailMessagePartPrivate::QMailMessagePartPrivate()
       
  4104     : QMailMessagePartContainerPrivate(this)
       
  4105 {
       
  4106 }
       
  4107 
       
  4108 QMailMessagePart::ReferenceType QMailMessagePartPrivate::referenceType() const
       
  4109 {
       
  4110     if (_referenceId.isValid())
       
  4111         return QMailMessagePart::MessageReference;
       
  4112         
       
  4113     if (_referenceLocation.isValid())
       
  4114         return QMailMessagePart::PartReference;
       
  4115 
       
  4116     return QMailMessagePart::None;
       
  4117 }
       
  4118 
       
  4119 QMailMessageId QMailMessagePartPrivate::messageReference() const
       
  4120 {
       
  4121     return _referenceId;
       
  4122 }
       
  4123 
       
  4124 QMailMessagePart::Location QMailMessagePartPrivate::partReference() const
       
  4125 {
       
  4126     return _referenceLocation;
       
  4127 }
       
  4128 
       
  4129 QString QMailMessagePartPrivate::referenceResolution() const
       
  4130 {
       
  4131     return _resolution;
       
  4132 }
       
  4133 
       
  4134 void QMailMessagePartPrivate::setReferenceResolution(const QString &uri)
       
  4135 {
       
  4136     _resolution = uri;
       
  4137 }
       
  4138 
       
  4139 bool QMailMessagePartPrivate::contentAvailable() const
       
  4140 {
       
  4141     if (_multipartType != QMailMessage::MultipartNone)
       
  4142         return true;
       
  4143 
       
  4144     if (_body.isEmpty())
       
  4145         return false;
       
  4146 
       
  4147     // Complete content is available only if the 'partial-content' header field is not present
       
  4148     QByteArray fieldName(internalPrefix() + "partial-content");
       
  4149     return (headerField(fieldName).isEmpty());
       
  4150 }
       
  4151 
       
  4152 bool QMailMessagePartPrivate::partialContentAvailable() const
       
  4153 {
       
  4154     return ((_multipartType != QMailMessage::MultipartNone) || !_body.isEmpty());
       
  4155 }
       
  4156 
       
  4157 template <typename F>
       
  4158 void QMailMessagePartPrivate::output(QDataStream **out, bool addMimePreamble, bool includeAttachments, bool excludeInternalFields, F *func) const
       
  4159 {
       
  4160     static const DataString newLine('\n');
       
  4161 
       
  4162     _header.output( **out, QList<QByteArray>(), excludeInternalFields );
       
  4163     **out << DataString('\n');
       
  4164 
       
  4165     QMailMessagePart::ReferenceType type(referenceType());
       
  4166     if (type == QMailMessagePart::None) {
       
  4167         if ( hasBody() ) {
       
  4168             outputBody( **out, includeAttachments );
       
  4169         } else {
       
  4170             outputParts<F>( out, addMimePreamble, includeAttachments, excludeInternalFields, func );
       
  4171         }
       
  4172     } else {
       
  4173         if (includeAttachments) {
       
  4174             // The next part must be a different chunk
       
  4175             if (func) {
       
  4176                 (*func)(QMailMessage::Text);
       
  4177             }
       
  4178 
       
  4179             if (!_resolution.isEmpty()) {
       
  4180                 **out << DataString(_resolution.toAscii());
       
  4181             } else {
       
  4182                 qWarning() << "QMailMessagePartPrivate::output - unresolved reference part!";
       
  4183             }
       
  4184 
       
  4185             if (func) {
       
  4186                 (*func)(QMailMessage::Reference);
       
  4187             }
       
  4188         }
       
  4189     }
       
  4190 }
       
  4191 
       
  4192 template <typename Stream> 
       
  4193 void QMailMessagePartPrivate::serialize(Stream &stream) const
       
  4194 {
       
  4195     QMailMessagePartContainerPrivate::serialize(stream);
       
  4196 
       
  4197     stream << _referenceId;
       
  4198     stream << _referenceLocation;
       
  4199     stream << _resolution;
       
  4200 }
       
  4201 
       
  4202 template <typename Stream> 
       
  4203 void QMailMessagePartPrivate::deserialize(Stream &stream)
       
  4204 {
       
  4205     QMailMessagePartContainerPrivate::deserialize(stream);
       
  4206 
       
  4207     stream >> _referenceId;
       
  4208     stream >> _referenceLocation;
       
  4209     stream >> _resolution;
       
  4210 }
       
  4211 
       
  4212 void QMailMessagePartPrivate::setReference(const QMailMessageId &id, 
       
  4213                                            const QMailMessageContentType& type, 
       
  4214                                            QMailMessageBody::TransferEncoding encoding)
       
  4215 {
       
  4216     _referenceId = id;
       
  4217     setBodyProperties(type, encoding);
       
  4218 }
       
  4219 
       
  4220 void QMailMessagePartPrivate::setReference(const QMailMessagePart::Location &location, 
       
  4221                                            const QMailMessageContentType& type, 
       
  4222                                            QMailMessageBody::TransferEncoding encoding)
       
  4223 {
       
  4224     _referenceLocation = location;
       
  4225     setBodyProperties(type, encoding);
       
  4226 }
       
  4227 
       
  4228 bool QMailMessagePartPrivate::contentModified() const
       
  4229 {
       
  4230     // Specific to this part
       
  4231     return dirty(false);
       
  4232 }
       
  4233 
       
  4234 void QMailMessagePartPrivate::setUnmodified()
       
  4235 {
       
  4236     setDirty(false, false);
       
  4237 }
       
  4238 
       
  4239 QMailMessagePartContainerPrivate* QMailMessagePartContainerPrivate::privatePointer(QMailMessagePart& part)
       
  4240 {
       
  4241     /* Nasty workaround required to access this data without detaching a copy... */
       
  4242     return const_cast<QMailMessagePartPrivate*>(static_cast<const QMailMessagePartPrivate*>(part.d.constData()));
       
  4243 }
       
  4244 
       
  4245 /*!
       
  4246     \fn bool QMailMessagePartContainer::foreachPart(F func)
       
  4247     
       
  4248     Applies the function or functor \a func to each part contained within the container.
       
  4249     \a func must implement the signature 'bool operator()(QMailMessagePart &)', and must
       
  4250     return true to indicate success, or false to end the traversal operation.
       
  4251 
       
  4252     Returns true if all parts of the message were traversed, and \a func returned true for
       
  4253     every invocation; else returns false.
       
  4254 */
       
  4255 
       
  4256 /*!
       
  4257     \fn bool QMailMessagePartContainer::foreachPart(F func) const
       
  4258     
       
  4259     Applies the function or functor \a func to each part contained within the container.
       
  4260     \a func must implement the signature 'bool operator()(const QMailMessagePart &)', and must
       
  4261     return true to indicate success, or false to end the traversal operation.
       
  4262 
       
  4263     Returns true if all parts of the message were traversed, and \a func returned true for
       
  4264     every invocation; else returns false.
       
  4265 */
       
  4266 
       
  4267 
       
  4268 //===========================================================================
       
  4269 /*      Mail Message Part   */
       
  4270 
       
  4271 /*!
       
  4272     \class QMailMessagePart
       
  4273 
       
  4274     \brief The QMailMessagePart class provides a convenient interface for working 
       
  4275     with message attachments.
       
  4276 
       
  4277     \ingroup messaginglibrary
       
  4278     
       
  4279     A message part inherits the properties of QMailMessagePartContainer, and can 
       
  4280     therefore contain a message body or a collection of sub-parts.  
       
  4281     
       
  4282     A message part differs from a message proper in that a part will often have 
       
  4283     properties specified by the MIME multipart specification, not relevant to 
       
  4284     messages.  These include the 'name' and 'filename' parameters of the Content-Type 
       
  4285     and Content-Disposition fields, and the Content-Id and Content-Location fields.
       
  4286 
       
  4287     A message part may consist entirely of a reference to an external message, or
       
  4288     a part within an external message.  Parts that consists of references may be 
       
  4289     used with some protocols that permit data to be transmitted by reference, such
       
  4290     as IMAP with the URLAUTH extension.  Not all messaging protocols support the
       
  4291     use of content references. The partReference() and messageReference() functions
       
  4292     enable the creation of reference parts.
       
  4293 
       
  4294     \sa QMailMessagePartContainer
       
  4295 */
       
  4296 
       
  4297 /*!
       
  4298     \typedef QMailMessagePart::ImplementationType
       
  4299     \internal
       
  4300 */
       
  4301 
       
  4302 
       
  4303 /*!
       
  4304     \class QMailMessagePart::Location
       
  4305 
       
  4306     \brief The Location class contains a specification of the location of a message part
       
  4307     with the message that contains it.
       
  4308 
       
  4309     \ingroup messaginglibrary
       
  4310     
       
  4311     A Location object is used to refer to a single part within a multi-part message.  The
       
  4312     location can be used to reference a part within a QMailMessage object, via the 
       
  4313     \l{QMailMessage::partAt()}{partAt} function.
       
  4314 */
       
  4315 
       
  4316 /*!
       
  4317     Creates an empty part location object.
       
  4318 */
       
  4319 QMailMessagePart::Location::Location()
       
  4320     : d(new QMailMessagePart::LocationPrivate)
       
  4321 {
       
  4322 }
       
  4323 
       
  4324 /*!
       
  4325     Creates a part location object referring to the location given by \a description.
       
  4326 
       
  4327     \sa toString()
       
  4328 */
       
  4329 QMailMessagePart::Location::Location(const QString& description)
       
  4330     : d(new QMailMessagePart::LocationPrivate)
       
  4331 {
       
  4332     QString indices;
       
  4333 
       
  4334     int separator = description.indexOf('-');
       
  4335     if (separator != -1) {
       
  4336         d->_messageId = QMailMessageId(description.left(separator).toULongLong());
       
  4337         indices = description.mid(separator + 1);
       
  4338     } else {
       
  4339         indices = description;
       
  4340     }
       
  4341 
       
  4342     if (!indices.isEmpty()) {
       
  4343         foreach (const QString &index, indices.split(".")) {
       
  4344             d->_indices.append(index.toUInt());
       
  4345         }
       
  4346     }
       
  4347 
       
  4348     Q_ASSERT(description == toString(separator == -1 ? false : true));
       
  4349 }
       
  4350 
       
  4351 /*!
       
  4352     Creates a part location object containing a copy of \a other.
       
  4353 */
       
  4354 QMailMessagePart::Location::Location(const Location& other)
       
  4355     : d(new QMailMessagePart::LocationPrivate)
       
  4356 {
       
  4357     *this = other;
       
  4358 }
       
  4359 
       
  4360 /*!
       
  4361     Creates a location object containing the location of \a part.
       
  4362 */
       
  4363 QMailMessagePart::Location::Location(const QMailMessagePart& part)
       
  4364     : d(new QMailMessagePart::LocationPrivate)
       
  4365 {
       
  4366     const QMailMessagePartContainerPrivate* partImpl = part.impl<const QMailMessagePartContainerPrivate>();
       
  4367 
       
  4368     d->_messageId = partImpl->_messageId;
       
  4369     d->_indices = partImpl->_indices;
       
  4370 }
       
  4371 
       
  4372 /*! \internal */
       
  4373 QMailMessagePart::Location::~Location()
       
  4374 {
       
  4375     delete d;
       
  4376 }
       
  4377 
       
  4378 /*! \internal */
       
  4379 const QMailMessagePart::Location &QMailMessagePart::Location::operator=(const QMailMessagePart::Location &other)
       
  4380 {
       
  4381     d->_messageId = other.d->_messageId;
       
  4382     d->_indices = other.d->_indices;
       
  4383 
       
  4384     return *this;
       
  4385 }
       
  4386 
       
  4387 /*! 
       
  4388     Returns true if the location object contains the location of a valid message part.
       
  4389     If \a extended is true, the location must also contain a valid message identifier.
       
  4390 */
       
  4391 bool QMailMessagePart::Location::isValid(bool extended) const
       
  4392 {
       
  4393     return ((!extended || d->_messageId.isValid()) && !d->_indices.isEmpty());
       
  4394 }
       
  4395 
       
  4396 /*! 
       
  4397     Returns the identifier of the message that contains the part with this location.
       
  4398 */
       
  4399 QMailMessageId QMailMessagePart::Location::containingMessageId() const
       
  4400 {
       
  4401     return d->_messageId;
       
  4402 }
       
  4403 
       
  4404 /*! 
       
  4405     Sets the identifier of the message that contains the part with this location to \a id.
       
  4406 */
       
  4407 void QMailMessagePart::Location::setContainingMessageId(const QMailMessageId &id)
       
  4408 {
       
  4409     d->_messageId = id;
       
  4410 }
       
  4411 
       
  4412 /*!
       
  4413     Returns a textual representation of the part location.
       
  4414     If \a extended is true, the representation contains the identifier of the containing message.
       
  4415 */
       
  4416 QString QMailMessagePart::Location::toString(bool extended) const
       
  4417 {
       
  4418     QString result;
       
  4419     if (extended)
       
  4420         result = QString::number(d->_messageId.toULongLong()) + '-';
       
  4421 
       
  4422     QStringList numbers;
       
  4423     foreach (uint index, d->_indices)
       
  4424         numbers.append(QString::number(index));
       
  4425 
       
  4426     return result.append(numbers.join("."));
       
  4427 }
       
  4428 
       
  4429 /*! 
       
  4430     \fn QMailMessagePart::Location::serialize(Stream&) const
       
  4431     \internal 
       
  4432 */
       
  4433 template <typename Stream> 
       
  4434 void QMailMessagePart::Location::serialize(Stream &stream) const
       
  4435 {
       
  4436     stream << d->_messageId;
       
  4437     stream << d->_indices;
       
  4438 }
       
  4439 
       
  4440 template void QMailMessagePart::Location::serialize(QDataStream &) const;
       
  4441 
       
  4442 /*! 
       
  4443     \fn QMailMessagePart::Location::deserialize(Stream&)
       
  4444     \internal 
       
  4445 */
       
  4446 template <typename Stream> 
       
  4447 void QMailMessagePart::Location::deserialize(Stream &stream)
       
  4448 {
       
  4449     stream >> d->_messageId;
       
  4450     stream >> d->_indices;
       
  4451 }
       
  4452 
       
  4453 template void QMailMessagePart::Location::deserialize(QDataStream &);
       
  4454 
       
  4455 /*!
       
  4456     Constructs an empty message part object.
       
  4457 */
       
  4458 QMailMessagePart::QMailMessagePart()
       
  4459     : QMailMessagePartContainer(new QMailMessagePartPrivate)
       
  4460 {
       
  4461 }
       
  4462 
       
  4463 /*!
       
  4464    Creates a QMailMessagePart containing an attachment of type \a disposition, from the 
       
  4465    data contained in \a filename, of content type \a type and using the transfer encoding
       
  4466    \a encoding.  The current status of the data is specified as \a status.
       
  4467 
       
  4468    \sa QMailMessageBody::fromFile()
       
  4469 */
       
  4470 QMailMessagePart QMailMessagePart::fromFile(const QString& filename,
       
  4471                                             const QMailMessageContentDisposition& disposition, 
       
  4472                                             const QMailMessageContentType& type, 
       
  4473                                             QMailMessageBody::TransferEncoding encoding, 
       
  4474                                             QMailMessageBody::EncodingStatus status)
       
  4475 {
       
  4476     QMailMessagePart part;
       
  4477     part.setBody( QMailMessageBody::fromFile( filename, type, encoding, status ) );
       
  4478     part.setContentDisposition( disposition );
       
  4479 
       
  4480     return part;
       
  4481 }
       
  4482 
       
  4483 /*!
       
  4484    Creates a QMailMessagePart containing an attachment of type \a disposition, from the 
       
  4485    data read from \a in, of content type \a type and using the transfer encoding
       
  4486    \a encoding.  The current status of the data is specified as \a status.
       
  4487 
       
  4488    \sa QMailMessageBody::fromStream()
       
  4489 */
       
  4490 QMailMessagePart QMailMessagePart::fromStream(QDataStream& in,
       
  4491                                               const QMailMessageContentDisposition& disposition, 
       
  4492                                               const QMailMessageContentType& type, 
       
  4493                                               QMailMessageBody::TransferEncoding encoding, 
       
  4494                                               QMailMessageBody::EncodingStatus status)
       
  4495 {
       
  4496     QMailMessagePart part;
       
  4497     part.setBody( QMailMessageBody::fromStream( in, type, encoding, status ) );
       
  4498     part.setContentDisposition( disposition );
       
  4499 
       
  4500     return part;
       
  4501 }
       
  4502 
       
  4503 /*!
       
  4504    Creates a QMailMessagePart containing an attachment of type \a disposition, from the 
       
  4505    data contained in \a input, of content type \a type and using the transfer encoding
       
  4506    \a encoding.  The current status of the data is specified as \a status.
       
  4507 
       
  4508    \sa QMailMessageBody::fromData()
       
  4509 */
       
  4510 QMailMessagePart QMailMessagePart::fromData(const QByteArray& input,
       
  4511                                             const QMailMessageContentDisposition& disposition, 
       
  4512                                             const QMailMessageContentType& type, 
       
  4513                                             QMailMessageBody::TransferEncoding encoding, 
       
  4514                                             QMailMessageBody::EncodingStatus status)
       
  4515 {
       
  4516     QMailMessagePart part;
       
  4517     part.setBody( QMailMessageBody::fromData( input, type, encoding, status ) );
       
  4518     part.setContentDisposition( disposition );
       
  4519 
       
  4520     return part;
       
  4521 }
       
  4522 
       
  4523 /*!
       
  4524    Creates a QMailMessagePart containing an attachment of type \a disposition, from the 
       
  4525    data read from \a in, of content type \a type and using the transfer encoding
       
  4526    \a encoding.
       
  4527 
       
  4528    \sa QMailMessageBody::fromStream()
       
  4529 */
       
  4530 QMailMessagePart QMailMessagePart::fromStream(QTextStream& in,
       
  4531                                               const QMailMessageContentDisposition& disposition, 
       
  4532                                               const QMailMessageContentType& type, 
       
  4533                                               QMailMessageBody::TransferEncoding encoding)
       
  4534 {
       
  4535     QMailMessagePart part;
       
  4536     part.setBody( QMailMessageBody::fromStream( in, type, encoding ) );
       
  4537     part.setContentDisposition( disposition );
       
  4538 
       
  4539     return part;
       
  4540 }
       
  4541 
       
  4542 /*!
       
  4543    Creates a QMailMessagePart containing an attachment of type \a disposition, from the 
       
  4544    data contained in \a input, of content type \a type and using the transfer encoding
       
  4545    \a encoding.
       
  4546 
       
  4547    \sa QMailMessageBody::fromData()
       
  4548 */
       
  4549 QMailMessagePart QMailMessagePart::fromData(const QString& input,
       
  4550                                             const QMailMessageContentDisposition& disposition, 
       
  4551                                             const QMailMessageContentType& type, 
       
  4552                                             QMailMessageBody::TransferEncoding encoding)
       
  4553 {
       
  4554     QMailMessagePart part;
       
  4555     part.setBody( QMailMessageBody::fromData( input, type, encoding ) );
       
  4556     part.setContentDisposition( disposition );
       
  4557 
       
  4558     return part;
       
  4559 }
       
  4560 
       
  4561 /*!
       
  4562     Creates a QMailMessagePart containing an attachment of type \a disposition, whose
       
  4563     content is a reference to the message identified by \a messageId.  The resulting 
       
  4564     part has content type \a type and uses the transfer encoding \a encoding.
       
  4565     
       
  4566     The message reference can only be resolved by transmitting the message to an external 
       
  4567     server, where both the originating server of the referenced message and the receiving 
       
  4568     server of the new message support resolution of the content reference.
       
  4569 */
       
  4570 QMailMessagePart QMailMessagePart::fromMessageReference(const QMailMessageId &messageId,
       
  4571                                                         const QMailMessageContentDisposition& disposition, 
       
  4572                                                         const QMailMessageContentType& type, 
       
  4573                                                         QMailMessageBody::TransferEncoding encoding)
       
  4574 {
       
  4575     QMailMessagePart part;
       
  4576     part.setReference(messageId, type, encoding);
       
  4577     part.setContentDisposition(disposition);
       
  4578 
       
  4579     return part;
       
  4580 }
       
  4581 
       
  4582 /*!
       
  4583     Creates a QMailMessagePart containing an attachment of type \a disposition, whose
       
  4584     content is a reference to the message part identified by \a partLocation.  The 
       
  4585     resulting part has content type \a type and uses the transfer encoding \a encoding.
       
  4586     
       
  4587     The part reference can only be resolved by transmitting the message to an external 
       
  4588     server, where both the originating server of the referenced part's message and the 
       
  4589     receiving server of the new message support resolution of the content reference.
       
  4590 */
       
  4591 QMailMessagePart QMailMessagePart::fromPartReference(const QMailMessagePart::Location &partLocation,
       
  4592                                                      const QMailMessageContentDisposition& disposition, 
       
  4593                                                      const QMailMessageContentType& type, 
       
  4594                                                      QMailMessageBody::TransferEncoding encoding)
       
  4595 {
       
  4596     QMailMessagePart part;
       
  4597     part.setReference(partLocation, type, encoding);
       
  4598     part.setContentDisposition(disposition);
       
  4599 
       
  4600     return part;
       
  4601 }
       
  4602 
       
  4603 /*!
       
  4604     Sets the part content to contain a reference to the message identified by \a id,
       
  4605     having content type \a type and using the transfer encoding \a encoding.
       
  4606     
       
  4607     The message reference can only be resolved by transmitting the message to an external 
       
  4608     server, where both the originating server of the referenced message and the receiving 
       
  4609     server of the new message support resolution of the content reference.
       
  4610 
       
  4611     \sa referenceType(), setReferenceResolution()
       
  4612 */
       
  4613 void QMailMessagePart::setReference(const QMailMessageId &id, const QMailMessageContentType& type, QMailMessageBody::TransferEncoding encoding)
       
  4614 {
       
  4615     impl(this)->setReference(id, type, encoding);
       
  4616 }
       
  4617 
       
  4618 /*!
       
  4619     Sets the part content to contain a reference to the message part identified by \a location,
       
  4620     having content type \a type and using the transfer encoding \a encoding.
       
  4621     
       
  4622     The part reference can only be resolved by transmitting the message to an external 
       
  4623     server, where both the originating server of the referenced part's message and the 
       
  4624     receiving server of the new message support resolution of the content reference.
       
  4625 
       
  4626     \sa referenceType(), setReferenceResolution()
       
  4627 */
       
  4628 void QMailMessagePart::setReference(const QMailMessagePart::Location &location, const QMailMessageContentType& type, QMailMessageBody::TransferEncoding encoding)
       
  4629 {
       
  4630     impl(this)->setReference(location, type, encoding);
       
  4631 }
       
  4632 
       
  4633 /*!
       
  4634     Returns the Content-Id header field for the part, if present; otherwise returns an empty string.
       
  4635 
       
  4636     If the header field content is surrounded by angle brackets, these are removed.
       
  4637 */
       
  4638 QString QMailMessagePart::contentID() const
       
  4639 {
       
  4640     QString result(headerFieldText("Content-ID"));
       
  4641     if (!result.isEmpty() && (result[0] == QChar('<')) && (result[result.length() - 1] == QChar('>'))) {
       
  4642         return result.mid(1, result.length() - 2);
       
  4643     }
       
  4644 
       
  4645     return result;
       
  4646 }
       
  4647 
       
  4648 /*!
       
  4649     Sets the Content-Id header field for the part to contain \a id.
       
  4650 
       
  4651     If \a id is not surrounded by angle brackets, these are added.
       
  4652 */
       
  4653 void QMailMessagePart::setContentID(const QString &id)
       
  4654 {
       
  4655     QString str(id);
       
  4656     if (!str.isEmpty()) {
       
  4657         if (str[0] != QChar('<')) {
       
  4658             str.prepend('<');
       
  4659         }
       
  4660         if (str[str.length() - 1] != QChar('>')) {
       
  4661             str.append('>');
       
  4662         }
       
  4663     }
       
  4664 
       
  4665     setHeaderField("Content-ID", str);
       
  4666 }
       
  4667 
       
  4668 /*!
       
  4669     Returns the Content-Location header field for the part, if present; 
       
  4670     otherwise returns an empty string.
       
  4671 */
       
  4672 QString QMailMessagePart::contentLocation() const
       
  4673 {
       
  4674     return headerFieldText("Content-Location");
       
  4675 }
       
  4676 
       
  4677 /*!
       
  4678     Sets the Content-Location header field for the part to contain \a location.
       
  4679 */
       
  4680 void QMailMessagePart::setContentLocation(const QString &location)
       
  4681 {
       
  4682     setHeaderField("Content-Location", location);
       
  4683 }
       
  4684 
       
  4685 /*!
       
  4686     Returns the Content-Description header field for the part, if present; 
       
  4687     otherwise returns an empty string.
       
  4688 */
       
  4689 QString QMailMessagePart::contentDescription() const
       
  4690 {
       
  4691     return headerFieldText("Content-Description");
       
  4692 }
       
  4693 
       
  4694 /*!
       
  4695     Sets the Content-Description header field for the part to contain \a description.
       
  4696 */
       
  4697 void QMailMessagePart::setContentDescription(const QString &description)
       
  4698 {
       
  4699     setHeaderField("Content-Description", description);
       
  4700 }
       
  4701 
       
  4702 /*!
       
  4703     Returns the Content-Disposition header field for the part.
       
  4704 */
       
  4705 QMailMessageContentDisposition QMailMessagePart::contentDisposition() const
       
  4706 {
       
  4707     return QMailMessageContentDisposition(headerField("Content-Disposition"));
       
  4708 }
       
  4709 
       
  4710 /*!
       
  4711     Sets the Content-Disposition header field for the part to contain \a disposition.
       
  4712 */
       
  4713 void QMailMessagePart::setContentDisposition(const QMailMessageContentDisposition &disposition)
       
  4714 {
       
  4715     setHeaderField("Content-Disposition", disposition.toString());
       
  4716 }
       
  4717 
       
  4718 /*!
       
  4719     Returns the Content-Language header field for the part, if present; 
       
  4720     otherwise returns an empty string.
       
  4721 */
       
  4722 QString QMailMessagePart::contentLanguage() const
       
  4723 {
       
  4724     return headerFieldText("Content-Language");
       
  4725 }
       
  4726 
       
  4727 /*!
       
  4728     Sets the Content-Language header field for the part to contain \a language.
       
  4729 */
       
  4730 void QMailMessagePart::setContentLanguage(const QString &language)
       
  4731 {
       
  4732     setHeaderField("Content-Language", language);
       
  4733 }
       
  4734 
       
  4735 /*!
       
  4736     Returns the number of the part, if it has been set; otherwise returns -1.
       
  4737 */
       
  4738 int QMailMessagePart::partNumber() const
       
  4739 {
       
  4740     return impl(this)->partNumber();
       
  4741 }
       
  4742 
       
  4743 /*!
       
  4744     Returns the location of the part within the message.
       
  4745 */
       
  4746 QMailMessagePart::Location QMailMessagePart::location() const
       
  4747 {
       
  4748     return QMailMessagePart::Location(*this);
       
  4749 }
       
  4750 
       
  4751 /*!
       
  4752     Returns a non-empty string to identify the part, appropriate for display.  If the part 
       
  4753     'Content-Type' header field contains a 'name' parameter, that value is used. Otherwise, 
       
  4754     if the part has a 'Content-Disposition' header field containing a 'filename' parameter, 
       
  4755     that value is used.  Otherwise, if the part has a 'Content-ID' header field, that value 
       
  4756     is used.  Finally, a usable name will be created by combining the content type of the 
       
  4757     part with the part's number.
       
  4758 
       
  4759     \sa identifier()
       
  4760 */
       
  4761 QString QMailMessagePart::displayName() const
       
  4762 {
       
  4763     QString id(contentType().name());
       
  4764 
       
  4765     if (id.isEmpty())
       
  4766         id = contentDisposition().filename();
       
  4767 
       
  4768     if (id.isEmpty())
       
  4769         id = contentID();
       
  4770 
       
  4771     if (id.isEmpty()) {
       
  4772         int partNumber = impl(this)->partNumber();
       
  4773         if (partNumber != -1) {
       
  4774             id = QString::number(partNumber) + " ";
       
  4775         }
       
  4776         id += contentType().content();
       
  4777     }
       
  4778 
       
  4779     return id;
       
  4780 }
       
  4781 
       
  4782 /*!
       
  4783     Returns a non-empty string to identify the part, appropriate for storage.  If the part 
       
  4784     has a 'Content-ID' header field, that value is used.  Otherwise, if the part has a 
       
  4785     'Content-Disposition' header field containing a 'filename' parameter, that value is used.
       
  4786     Otherwise, if the part 'Content-Type' header field contains a 'name' parameter, that value 
       
  4787     is used.  Finally, the part's number will be returned.
       
  4788 */
       
  4789 QString QMailMessagePart::identifier() const
       
  4790 {
       
  4791     QString id(contentID());
       
  4792 
       
  4793     if (id.isEmpty())
       
  4794         id = contentDisposition().filename();
       
  4795 
       
  4796     if (id.isEmpty())
       
  4797         id = contentType().name();
       
  4798 
       
  4799     if (id.isEmpty())
       
  4800         id = QString::number(impl(this)->partNumber());
       
  4801 
       
  4802     return id;
       
  4803 }
       
  4804 
       
  4805 /*!
       
  4806     Returns the type of reference that this message part constitutes.
       
  4807 
       
  4808     \sa setReference()
       
  4809 */
       
  4810 QMailMessagePart::ReferenceType QMailMessagePart::referenceType() const
       
  4811 {
       
  4812     return impl(this)->referenceType();
       
  4813 }
       
  4814 
       
  4815 /*!
       
  4816     Returns the identifier of the message that this part references.
       
  4817 
       
  4818     The result will be meaningful only when referenceType() yields
       
  4819     \l{QMailMessagePartFwd::MessageReference}{QMailMessagePart::MessageReference}.
       
  4820 
       
  4821     \sa referenceType(), partReference(), referenceResolution()
       
  4822 */
       
  4823 QMailMessageId QMailMessagePart::messageReference() const
       
  4824 {
       
  4825     return impl(this)->messageReference();
       
  4826 }
       
  4827 
       
  4828 /*!
       
  4829     Returns the location of the message part that this part references.
       
  4830 
       
  4831     The result will be meaningful only when referenceType() yields
       
  4832     \l{QMailMessagePartFwd::PartReference}{QMailMessagePart::PartReference}.
       
  4833 
       
  4834     \sa referenceType(), messageReference(), referenceResolution()
       
  4835 */
       
  4836 QMailMessagePart::Location QMailMessagePart::partReference() const
       
  4837 {
       
  4838     return impl(this)->partReference();
       
  4839 }
       
  4840 
       
  4841 /*!
       
  4842     Returns the URI that resolves the reference encoded into this message part.
       
  4843 
       
  4844     The result will be meaningful only when referenceType() yields other than
       
  4845     \l{QMailMessagePartFwd::None}{QMailMessagePart::None}.
       
  4846 
       
  4847     \sa setReferenceResolution(), referenceType()
       
  4848 */
       
  4849 QString QMailMessagePart::referenceResolution() const
       
  4850 {
       
  4851     return impl(this)->referenceResolution();
       
  4852 }
       
  4853 
       
  4854 /*!
       
  4855     Sets the URI that resolves the reference encoded into this message part to \a uri.
       
  4856 
       
  4857     The reference URI is meaningful only when referenceType() yields other than
       
  4858     \l{QMailMessagePartFwd::None}{QMailMessagePart::None}.
       
  4859 
       
  4860     \sa referenceResolution(), referenceType()
       
  4861 */
       
  4862 void QMailMessagePart::setReferenceResolution(const QString &uri)
       
  4863 {
       
  4864     impl(this)->setReferenceResolution(uri);
       
  4865 }
       
  4866 
       
  4867 static QString randomString(int length)
       
  4868 {
       
  4869     if (length <= 0) 
       
  4870         return QString();
       
  4871 
       
  4872     QString str;
       
  4873     str.resize( length );
       
  4874 
       
  4875     int i = 0;
       
  4876     while (length--){
       
  4877         int r=qrand() % 62;
       
  4878         r+=48;
       
  4879         if (r>57) r+=7;
       
  4880         if (r>90) r+=6;
       
  4881         str[i++] = char(r);
       
  4882     }
       
  4883     return str;
       
  4884 }
       
  4885 
       
  4886 static QString partFileName(const QMailMessagePart &part)
       
  4887 {
       
  4888     QString fileName(part.identifier());
       
  4889     if (!fileName.isEmpty()) {
       
  4890         // Remove any slash characters which are invalid in filenames
       
  4891         QChar* first = fileName.data(), *last = first + (fileName.length() - 1);
       
  4892         for ( ; last >= first; --last)
       
  4893             if (*last == '/')
       
  4894                 fileName.remove((last - first), 1);
       
  4895     }
       
  4896 
       
  4897     // If possible, create the file with a useful filename extension
       
  4898     QString existing;
       
  4899     int index = fileName.lastIndexOf(".");
       
  4900     if (index != -1)
       
  4901         existing = fileName.mid(index + 1);
       
  4902 
       
  4903     QStringList extensions = QMail::extensionsForMimeType(part.contentType().content());
       
  4904     if (!extensions.isEmpty()) {
       
  4905         // See if the existing extension is a known one
       
  4906         if (existing.isEmpty() || !extensions.contains(existing, Qt::CaseInsensitive)) {
       
  4907             if (!fileName.endsWith(".")) {
       
  4908                 fileName.append(".");
       
  4909             }
       
  4910             fileName.append(extensions.first());
       
  4911         }
       
  4912     }
       
  4913 
       
  4914     return fileName;
       
  4915 }
       
  4916 
       
  4917 /*!
       
  4918     Writes the decoded body of the part to a file under the directory specified by \a path.
       
  4919     The name of the resulting file is taken from the part. If that file name already exists 
       
  4920     in the path a new unique name of the format <random chars>.<filename> is saved.
       
  4921 
       
  4922     Returns the path of the file written on success, or an empty string otherwise.
       
  4923 */
       
  4924 
       
  4925 QString QMailMessagePart::writeBodyTo(const QString &path) const
       
  4926 {
       
  4927     QString directory(path);
       
  4928     if (directory.endsWith("/"))
       
  4929         directory.chop(1);
       
  4930 
       
  4931     if (!QDir(directory).exists()) {
       
  4932         QDir base;
       
  4933         if (QDir::isAbsolutePath(directory))
       
  4934             base = QDir::root();
       
  4935         else
       
  4936             base = QDir::current();
       
  4937 
       
  4938         if (!base.mkpath(directory)) {
       
  4939             qWarning() << "Could not create directory to save file " << directory;
       
  4940             return QString();
       
  4941         }
       
  4942     }
       
  4943 
       
  4944     QString fileName(partFileName(*this));
       
  4945 
       
  4946     QString filepath = directory + "/" + fileName;
       
  4947     while (QFile::exists(filepath))
       
  4948         filepath = directory + "/" + randomString(5) + "." + fileName;
       
  4949 
       
  4950     if (!body().toFile(filepath, QMailMessageBody::Decoded)) {
       
  4951         qWarning() << "Could not write part data to file " << filepath;
       
  4952         return QString();
       
  4953     }
       
  4954     
       
  4955     return filepath;
       
  4956 }
       
  4957 
       
  4958 /*!
       
  4959     Returns an indication of the size of the part.  This measure should be used
       
  4960     only in comparing the relative size of parts with respect to transmission.
       
  4961 */  
       
  4962 uint QMailMessagePart::indicativeSize() const
       
  4963 {
       
  4964     return impl(this)->indicativeSize();
       
  4965 }
       
  4966 
       
  4967 /*!
       
  4968     Returns true if the entire content of this part is available; otherwise returns false.
       
  4969 */
       
  4970 bool QMailMessagePart::contentAvailable() const
       
  4971 {
       
  4972     return impl(this)->contentAvailable();
       
  4973 }
       
  4974 
       
  4975 /*!
       
  4976     Returns true if some portion of the content of this part is available; otherwise returns false.
       
  4977 */
       
  4978 bool QMailMessagePart::partialContentAvailable() const
       
  4979 {
       
  4980     return impl(this)->partialContentAvailable();
       
  4981 }
       
  4982 
       
  4983 /*! \internal */
       
  4984 void QMailMessagePart::output(QDataStream& out, bool includeAttachments, bool excludeInternalFields) const
       
  4985 {
       
  4986     QDataStream *ds(&out);
       
  4987     return impl(this)->output<DummyChunkProcessor>(&ds, false, includeAttachments, excludeInternalFields, 0);
       
  4988 }
       
  4989 
       
  4990 /*! 
       
  4991     \fn QMailMessagePart::serialize(Stream&) const
       
  4992     \internal 
       
  4993 */
       
  4994 template <typename Stream> 
       
  4995 void QMailMessagePart::serialize(Stream &stream) const
       
  4996 {
       
  4997     impl(this)->serialize(stream);
       
  4998 }
       
  4999 
       
  5000 template void QMailMessagePart::serialize(QDataStream &) const;
       
  5001 
       
  5002 /*! 
       
  5003     \fn QMailMessagePart::deserialize(Stream&)
       
  5004     \internal 
       
  5005 */
       
  5006 template <typename Stream> 
       
  5007 void QMailMessagePart::deserialize(Stream &stream)
       
  5008 {
       
  5009     impl(this)->deserialize(stream);
       
  5010 }
       
  5011 
       
  5012 template void QMailMessagePart::deserialize(QDataStream &);
       
  5013 
       
  5014 /*! \internal */
       
  5015 bool QMailMessagePart::contentModified() const
       
  5016 {
       
  5017     return impl(this)->contentModified();
       
  5018 }
       
  5019 
       
  5020 /*! \internal */
       
  5021 void QMailMessagePart::setUnmodified()
       
  5022 {
       
  5023     impl(this)->setUnmodified();
       
  5024 }
       
  5025 
       
  5026 
       
  5027 static quint64 incomingFlag = 0;
       
  5028 static quint64 outgoingFlag = 0;
       
  5029 static quint64 sentFlag = 0;
       
  5030 static quint64 repliedFlag = 0;
       
  5031 static quint64 repliedAllFlag = 0;
       
  5032 static quint64 forwardedFlag = 0;
       
  5033 static quint64 contentAvailableFlag = 0;
       
  5034 static quint64 readFlag = 0;
       
  5035 static quint64 removedFlag = 0;
       
  5036 static quint64 readElsewhereFlag = 0;
       
  5037 static quint64 unloadedDataFlag = 0;
       
  5038 static quint64 newFlag = 0;
       
  5039 static quint64 readReplyRequestedFlag = 0;
       
  5040 static quint64 trashFlag = 0;
       
  5041 static quint64 partialContentAvailableFlag = 0;
       
  5042 static quint64 hasAttachmentsFlag = 0;
       
  5043 static quint64 hasReferencesFlag = 0;
       
  5044 static quint64 hasUnresolvedReferencesFlag = 0;
       
  5045 static quint64 draftFlag = 0;
       
  5046 static quint64 outboxFlag = 0;
       
  5047 static quint64 junkFlag = 0;
       
  5048 static quint64 transmitFromExternalFlag = 0;
       
  5049 static quint64 localOnlyFlag = 0;
       
  5050 
       
  5051 
       
  5052 /*  QMailMessageMetaData */
       
  5053 
       
  5054 QMailMessageMetaDataPrivate::QMailMessageMetaDataPrivate()
       
  5055     : QPrivateImplementationBase(this),
       
  5056       _messageType(QMailMessage::None),
       
  5057       _status(0),
       
  5058       _contentType(QMailMessage::UnknownContent),
       
  5059       _size(0),
       
  5060       _responseType(QMailMessage::NoResponse),
       
  5061       _customFieldsModified(false),
       
  5062       _dirty(false)
       
  5063 {
       
  5064 }
       
  5065 
       
  5066 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5067 void QMailMessageMetaDataPrivate::initializeFlags()
       
  5068 {
       
  5069     static bool flagsInitialized = false;
       
  5070     if (!flagsInitialized) {
       
  5071         flagsInitialized = true;
       
  5072 
       
  5073         incomingFlag = registerFlag("Incoming");
       
  5074         outgoingFlag = registerFlag("Outgoing");
       
  5075         sentFlag = registerFlag("Sent");
       
  5076         repliedFlag = registerFlag("Replied");
       
  5077         repliedAllFlag = registerFlag("RepliedAll");
       
  5078         forwardedFlag = registerFlag("Forwarded");
       
  5079         contentAvailableFlag = registerFlag("ContentAvailable");
       
  5080         readFlag = registerFlag("Read");
       
  5081         removedFlag = registerFlag("Removed");
       
  5082         readElsewhereFlag = registerFlag("ReadElsewhere");
       
  5083         unloadedDataFlag = registerFlag("UnloadedData");
       
  5084         newFlag = registerFlag("New");
       
  5085         readReplyRequestedFlag = registerFlag("ReadReplyRequested");
       
  5086         trashFlag = registerFlag("Trash");
       
  5087         partialContentAvailableFlag = registerFlag("PartialContentAvailable");
       
  5088         hasAttachmentsFlag = registerFlag("HasAttachments");
       
  5089         hasReferencesFlag = registerFlag("HasReferences");
       
  5090         hasUnresolvedReferencesFlag = registerFlag("HasUnresolvedReferences");
       
  5091         draftFlag = registerFlag("Draft");
       
  5092         outboxFlag = registerFlag("Outbox");
       
  5093         junkFlag = registerFlag("Junk");
       
  5094         transmitFromExternalFlag = registerFlag("TransmitFromExternal");
       
  5095         localOnlyFlag = registerFlag("LocalOnly");
       
  5096     }
       
  5097 }
       
  5098 #endif
       
  5099 
       
  5100 void QMailMessageMetaDataPrivate::setMessageType(QMailMessage::MessageType type)
       
  5101 {
       
  5102     updateMember(_messageType, type);
       
  5103 }
       
  5104 
       
  5105 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5106 void QMailMessageMetaDataPrivate::setParentFolderId(const QMailFolderId& id)
       
  5107 {
       
  5108     updateMember(_parentFolderId, id);
       
  5109 }
       
  5110 
       
  5111 void QMailMessageMetaDataPrivate::setPreviousParentFolderId(const QMailFolderId& id)
       
  5112 {
       
  5113     updateMember(_previousParentFolderId, id);
       
  5114 }
       
  5115 #endif
       
  5116 
       
  5117 void QMailMessageMetaDataPrivate::setId(const QMailMessageId& id)
       
  5118 {
       
  5119     updateMember(_id, id);
       
  5120 }
       
  5121 
       
  5122 void QMailMessageMetaDataPrivate::setStatus(quint64 newStatus)
       
  5123 {
       
  5124     updateMember(_status, newStatus);
       
  5125 }
       
  5126 
       
  5127 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5128 void QMailMessageMetaDataPrivate::setParentAccountId(const QMailAccountId& id)
       
  5129 {
       
  5130     updateMember(_parentAccountId, id);
       
  5131 }
       
  5132 #endif
       
  5133 
       
  5134 void QMailMessageMetaDataPrivate::setServerUid(const QString &uid)
       
  5135 {
       
  5136     updateMember(_serverUid, uid);
       
  5137 }
       
  5138 
       
  5139 void QMailMessageMetaDataPrivate::setSize(uint size)
       
  5140 {
       
  5141     updateMember(_size, size);
       
  5142 }
       
  5143 
       
  5144 void QMailMessageMetaDataPrivate::setContent(QMailMessage::ContentType type)
       
  5145 {
       
  5146     updateMember(_contentType, type);
       
  5147 }
       
  5148 
       
  5149 void QMailMessageMetaDataPrivate::setSubject(const QString& s)
       
  5150 {
       
  5151     updateMember(_subject, s);
       
  5152 }
       
  5153 
       
  5154 void QMailMessageMetaDataPrivate::setDate(const QMailTimeStamp& timeStamp)
       
  5155 {
       
  5156     updateMember(_date, timeStamp);
       
  5157 }
       
  5158 
       
  5159 void QMailMessageMetaDataPrivate::setReceivedDate(const QMailTimeStamp& timeStamp)
       
  5160 {
       
  5161     updateMember(_receivedDate, timeStamp);
       
  5162 }
       
  5163 
       
  5164 void QMailMessageMetaDataPrivate::setFrom(const QString& s)
       
  5165 {
       
  5166     updateMember(_from, s);
       
  5167 } 
       
  5168 
       
  5169 void QMailMessageMetaDataPrivate::setTo(const QString& s)
       
  5170 {
       
  5171     updateMember(_to, s);
       
  5172 }
       
  5173 
       
  5174 void QMailMessageMetaDataPrivate::setContentScheme(const QString& scheme)
       
  5175 {
       
  5176     updateMember(_contentScheme, scheme);
       
  5177 }
       
  5178 
       
  5179 void QMailMessageMetaDataPrivate::setContentIdentifier(const QString& identifier)
       
  5180 {
       
  5181     updateMember(_contentIdentifier, identifier);
       
  5182 }
       
  5183 
       
  5184 void QMailMessageMetaDataPrivate::setInResponseTo(const QMailMessageId &id)
       
  5185 {
       
  5186     updateMember(_responseId, id);
       
  5187 }
       
  5188 
       
  5189 void QMailMessageMetaDataPrivate::setResponseType(QMailMessageMetaData::ResponseType type)
       
  5190 {
       
  5191     updateMember(_responseType, type);
       
  5192 }
       
  5193 
       
  5194 uint QMailMessageMetaDataPrivate::indicativeSize() const
       
  5195 {
       
  5196     uint size = (_size / QMailMessageBodyPrivate::IndicativeSizeUnit);
       
  5197 
       
  5198     // Count the message header as one size unit
       
  5199     return (size + 1);
       
  5200 }
       
  5201 
       
  5202 bool QMailMessageMetaDataPrivate::dataModified() const
       
  5203 {
       
  5204     return _dirty || _customFieldsModified;
       
  5205 }
       
  5206 
       
  5207 void QMailMessageMetaDataPrivate::setUnmodified()
       
  5208 {
       
  5209     _dirty = false;
       
  5210     _customFieldsModified = false;
       
  5211 }
       
  5212 
       
  5213 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5214 quint64 QMailMessageMetaDataPrivate::registerFlag(const QString &name)
       
  5215 {
       
  5216     if (!QMailStore::instance()->registerMessageStatusFlag(name)) {
       
  5217         qMailLog(Messaging) << "Unable to register message status flag:" << name << "!";
       
  5218     }
       
  5219 
       
  5220     return QMailMessage::statusMask(name);
       
  5221 }
       
  5222 #endif
       
  5223 
       
  5224 QString QMailMessageMetaDataPrivate::customField(const QString &name) const
       
  5225 {
       
  5226     QMap<QString, QString>::const_iterator it = _customFields.find(name);
       
  5227     if (it != _customFields.end()) {
       
  5228         return *it;
       
  5229     }
       
  5230 
       
  5231     return QString();
       
  5232 }
       
  5233 
       
  5234 void QMailMessageMetaDataPrivate::setCustomField(const QString &name, const QString &value)
       
  5235 {
       
  5236     QMap<QString, QString>::iterator it = _customFields.find(name);
       
  5237     if (it != _customFields.end()) {
       
  5238         if (*it != value) {
       
  5239             *it = value;
       
  5240             _customFieldsModified = true;
       
  5241         }
       
  5242     } else {
       
  5243         _customFields.insert(name, value);
       
  5244         _customFieldsModified = true;
       
  5245     }
       
  5246 }
       
  5247 
       
  5248 void QMailMessageMetaDataPrivate::removeCustomField(const QString &name)
       
  5249 {
       
  5250     QMap<QString, QString>::iterator it = _customFields.find(name);
       
  5251     if (it != _customFields.end()) {
       
  5252         _customFields.erase(it);
       
  5253         _customFieldsModified = true;
       
  5254     }
       
  5255 }
       
  5256 
       
  5257 void QMailMessageMetaDataPrivate::setCustomFields(const QMap<QString, QString> &fields)
       
  5258 {
       
  5259     _customFields = fields;
       
  5260     _customFieldsModified = true;
       
  5261 }
       
  5262 
       
  5263 template <typename Stream> 
       
  5264 void QMailMessageMetaDataPrivate::serialize(Stream &stream) const
       
  5265 {
       
  5266     stream << _messageType;
       
  5267     stream << _status;
       
  5268     stream << _contentType;
       
  5269 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5270     stream << _parentAccountId;
       
  5271 #endif
       
  5272     stream << _serverUid;
       
  5273     stream << _size;
       
  5274     stream << _id;
       
  5275 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5276     stream << _parentFolderId;
       
  5277     stream << _previousParentFolderId;
       
  5278 #endif
       
  5279     stream << _subject;
       
  5280     stream << _date.toString();
       
  5281     stream << _receivedDate.toString();
       
  5282     stream << _from;
       
  5283     stream << _to;
       
  5284     stream << _contentScheme;
       
  5285     stream << _contentIdentifier;
       
  5286     stream << _responseId;
       
  5287     stream << _responseType;
       
  5288     stream << _customFields;
       
  5289     stream << _customFieldsModified;
       
  5290     stream << _dirty;
       
  5291 }
       
  5292 
       
  5293 template <typename Stream> 
       
  5294 void QMailMessageMetaDataPrivate::deserialize(Stream &stream)
       
  5295 {
       
  5296     QString timeStamp;
       
  5297 
       
  5298     stream >> _messageType;
       
  5299     stream >> _status;
       
  5300     stream >> _contentType;
       
  5301 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5302     stream >> _parentAccountId;
       
  5303 #endif
       
  5304     stream >> _serverUid;
       
  5305     stream >> _size;
       
  5306     stream >> _id;
       
  5307 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5308     stream >> _parentFolderId;
       
  5309     stream >> _previousParentFolderId;
       
  5310 #endif
       
  5311     stream >> _subject;
       
  5312     stream >> timeStamp;
       
  5313     _date = QMailTimeStamp(timeStamp);
       
  5314     stream >> timeStamp;
       
  5315     _receivedDate = QMailTimeStamp(timeStamp);
       
  5316     stream >> _from;
       
  5317     stream >> _to;
       
  5318     stream >> _contentScheme;
       
  5319     stream >> _contentIdentifier;
       
  5320     stream >> _responseId;
       
  5321     stream >> _responseType;
       
  5322     stream >> _customFields;
       
  5323     stream >> _customFieldsModified;
       
  5324     stream >> _dirty;
       
  5325 }
       
  5326 
       
  5327 
       
  5328 /*!
       
  5329     \class QMailMessageMetaData
       
  5330 
       
  5331     \brief The QMailMessageMetaData class provides information about a message stored by Qtopia.
       
  5332     
       
  5333     \ingroup messaginglibrary
       
  5334    
       
  5335     The QMailMessageMetaData class provides information about messages stored in the Qt Extended system as QMailMessage objects.  The meta data is more compact and more easily accessed and
       
  5336     manipulated than the content of the message itself.  Many messaging-related tasks can 
       
  5337     be accomplished by manipulating the message meta data, such as listing, filtering, and 
       
  5338     searching through sets of messages.
       
  5339 
       
  5340     QMailMessageMetaData objects can be created as needed, specifying the identifier of
       
  5341     the message whose meta data is required.  The meta data of a message can be located by 
       
  5342     specifying the QMailMessageId identifier directly, or by specifying the account and server UID 
       
  5343     pair needed to locate the message.
       
  5344 
       
  5345     The content of the message described by the meta data object can be accessed by creating 
       
  5346     a QMailMessage object specifying the identifier returned by QMailMessageMetaData::id().
       
  5347 
       
  5348     \sa QMailStore, QMailMessageId
       
  5349 */    
       
  5350     
       
  5351 /*!
       
  5352     \typedef QMailMessageMetaData::ImplementationType
       
  5353     \internal
       
  5354 */
       
  5355 
       
  5356 /*!
       
  5357     \variable QMailMessageMetaData::Incoming
       
  5358 
       
  5359     The status mask needed for testing the value of the registered status flag named 
       
  5360     \c "Incoming" against the result of QMailMessage::status().
       
  5361 
       
  5362     This flag indicates that the message has been sent from an external source to an
       
  5363     account whose messages are retrieved to Qt Extended.
       
  5364 */
       
  5365 
       
  5366 /*!
       
  5367     \variable QMailMessageMetaData::Outgoing
       
  5368 
       
  5369     The status mask needed for testing the value of the registered status flag named 
       
  5370     \c "Outgoing" against the result of QMailMessage::status().
       
  5371 
       
  5372     This flag indicates that the message originates within Qt Extended, for transmission 
       
  5373     to an external message sink.
       
  5374 */
       
  5375 
       
  5376 /*!
       
  5377     \variable QMailMessageMetaData::Sent
       
  5378 
       
  5379     The status mask needed for testing the value of the registered status flag named 
       
  5380     \c "Sent" against the result of QMailMessage::status().
       
  5381 
       
  5382     This flag indicates that the message has been delivered to an external message sink.
       
  5383 */
       
  5384 
       
  5385 /*!
       
  5386     \variable QMailMessageMetaData::Replied
       
  5387 
       
  5388     The status mask needed for testing the value of the registered status flag named 
       
  5389     \c "Replied" against the result of QMailMessage::status().
       
  5390 
       
  5391     This flag indicates that a message replying to the source of this message has been 
       
  5392     created, in response to this message.
       
  5393 */
       
  5394 
       
  5395 /*!
       
  5396     \variable QMailMessageMetaData::RepliedAll
       
  5397 
       
  5398     The status mask needed for testing the value of the registered status flag named 
       
  5399     \c "RepliedAll" against the result of QMailMessage::status().
       
  5400 
       
  5401     This flag indicates that a message replying to the source of this message and all 
       
  5402     its recipients, has been created in response to this message.
       
  5403 */
       
  5404 
       
  5405 /*!
       
  5406     \variable QMailMessageMetaData::Forwarded
       
  5407 
       
  5408     The status mask needed for testing the value of the registered status flag named 
       
  5409     \c "Forwarded" against the result of QMailMessage::status().
       
  5410 
       
  5411     This flag indicates that a message forwarding the content of this message has been created.
       
  5412 */
       
  5413 
       
  5414 /*!
       
  5415     \variable QMailMessageMetaData::Read
       
  5416 
       
  5417     The status mask needed for testing the value of the registered status flag named 
       
  5418     \c "Read" against the result of QMailMessage::status().
       
  5419 
       
  5420     This flag indicates that the content of this message has been displayed to the user.
       
  5421 */
       
  5422 
       
  5423 /*!
       
  5424     \variable QMailMessageMetaData::Removed
       
  5425 
       
  5426     The status mask needed for testing the value of the registered status flag named 
       
  5427     \c "Removed" against the result of QMailMessage::status().
       
  5428 
       
  5429     This flag indicates that the message has been deleted from or moved on the originating server.
       
  5430 */
       
  5431 
       
  5432 /*!
       
  5433     \variable QMailMessageMetaData::ReadElsewhere
       
  5434 
       
  5435     The status mask needed for testing the value of the registered status flag named 
       
  5436     \c "ReadElsewhere" against the result of QMailMessage::status().
       
  5437 
       
  5438     This flag indicates that the content of this message has been reported as having
       
  5439     been displayed to the user by some other client.
       
  5440 */
       
  5441 
       
  5442 /*!
       
  5443     \variable QMailMessageMetaData::UnloadedData
       
  5444 
       
  5445     The status mask needed for testing the value of the registered status flag named 
       
  5446     \c "UnloadedData" against the result of QMailMessage::status().
       
  5447 
       
  5448     This flag indicates that the meta data of the message is not loaded in entirety.
       
  5449 */
       
  5450 
       
  5451 /*!
       
  5452     \variable QMailMessageMetaData::New
       
  5453 
       
  5454     The status mask needed for testing the value of the registered status flag named 
       
  5455     \c "New" against the result of QMailMessage::status().
       
  5456 
       
  5457     This flag indicates that the meta data of the message has not yet been displayed to the user.
       
  5458 */
       
  5459 
       
  5460 /*!
       
  5461     \variable QMailMessageMetaData::ReadReplyRequested
       
  5462 
       
  5463     The status mask needed for testing the value of the registered status flag named 
       
  5464     \c "ReadReplyRequested" against the result of QMailMessage::status().
       
  5465 
       
  5466     This flag indicates that the message has requested that a read confirmation reply be returned to the sender.
       
  5467 */
       
  5468 
       
  5469 /*!
       
  5470     \variable QMailMessageMetaData::Trash
       
  5471 
       
  5472     The status mask needed for testing the value of the registered status flag named 
       
  5473     \c "Trash" against the result of QMailMessage::status().
       
  5474 
       
  5475     This flag indicates that the message has been marked as trash, and should be considered logically deleted.
       
  5476 */
       
  5477 
       
  5478 /*!
       
  5479     \variable QMailMessageMetaData::ContentAvailable
       
  5480 
       
  5481     The status mask needed for testing the value of the registered status flag named 
       
  5482     \c "ContentAvailable" against the result of QMailMessage::status().
       
  5483 
       
  5484     This flag indicates that the entire content of the message has been retrieved from the originating server,
       
  5485     excluding any sub-parts of the message.
       
  5486 
       
  5487     \sa QMailMessagePartContainer::contentAvailable()
       
  5488 */
       
  5489 
       
  5490 /*!
       
  5491     \variable QMailMessageMetaData::PartialContentAvailable
       
  5492 
       
  5493     The status mask needed for testing the value of the registered status flag named 
       
  5494     \c "PartialContentAvailable" against the result of QMailMessage::status().
       
  5495 
       
  5496     This flag indicates that some portion of the  content of the message has been retrieved from the originating server.
       
  5497 
       
  5498     \sa QMailMessagePartContainer::contentAvailable()
       
  5499 */
       
  5500 
       
  5501 /*!
       
  5502     \variable QMailMessageMetaData::HasAttachments
       
  5503 
       
  5504     The status mask needed for testing the value of the registered status flag named 
       
  5505     \c "HasAttachments" against the result of QMailMessage::status().
       
  5506 
       
  5507     This flag indicates that the message contains at least one sub-part with 'Attachment' disposition.
       
  5508 
       
  5509     \sa QMailMessageContentDisposition
       
  5510 */
       
  5511 
       
  5512 /*!
       
  5513     \variable QMailMessageMetaData::HasReferences
       
  5514 
       
  5515     The status mask needed for testing the value of the registered status flag named 
       
  5516     \c "HasReferences" against the result of QMailMessage::status().
       
  5517 
       
  5518     This flag indicates that the message contains at least one sub-part which is a reference to an external message element.
       
  5519 
       
  5520     \sa QMailMessagePart::referenceType()
       
  5521 */
       
  5522 
       
  5523 /*!
       
  5524     \variable QMailMessageMetaData::HasUnresolvedReferences
       
  5525 
       
  5526     The status mask needed for testing the value of the registered status flag named 
       
  5527     \c "HasUnresolvedReferences" against the result of QMailMessage::status().
       
  5528 
       
  5529     This flag indicates that the message contains at least one sub-part which is a reference, that has no corresponding resolution value.
       
  5530 
       
  5531     \sa QMailMessagePart::referenceType(), QMailMessagePart::referenceResolution()
       
  5532 */
       
  5533 
       
  5534 /*!
       
  5535     \variable QMailMessageMetaData::Draft
       
  5536 
       
  5537     The status mask needed for testing the value of the registered status flag named 
       
  5538     \c "Draft" against the result of QMailMessage::status().
       
  5539 
       
  5540     This flag indicates that the message has been marked as a draft, and should be considered subject to further composition.
       
  5541 */
       
  5542 
       
  5543 /*!
       
  5544     \variable QMailMessageMetaData::Outbox
       
  5545 
       
  5546     The status mask needed for testing the value of the registered status flag named 
       
  5547     \c "Outbox" against the result of QMailMessage::status().
       
  5548 
       
  5549     This flag indicates that the message has been marked as ready for transmission.
       
  5550 */
       
  5551 
       
  5552 /*!
       
  5553     \variable QMailMessageMetaData::Junk
       
  5554 
       
  5555     The status mask needed for testing the value of the registered status flag named 
       
  5556     \c "Junk" against the result of QMailMessage::status().
       
  5557 
       
  5558     This flag indicates that the message has been marked as junk, and should be considered unsuitable for standard listings.
       
  5559 */
       
  5560 
       
  5561 /*!
       
  5562     \variable QMailMessageMetaData::TransmitFromExternal
       
  5563 
       
  5564     The status mask needed for testing the value of the registered status flag named 
       
  5565     \c "TransmitFromExternal" against the result of QMailMessage::status().
       
  5566 
       
  5567     This flag indicates that the message should be transmitted by reference to its external server location.
       
  5568 */
       
  5569 
       
  5570 /*!
       
  5571     \variable QMailMessageMetaData::LocalOnly
       
  5572 
       
  5573     The status mask needed for testing the value of the registered status flag named 
       
  5574     \c "LocalOnly" against the result of QMailMessage::status().
       
  5575 
       
  5576     This flag indicates that the message exists only on the local device, and has no representation on any external server.
       
  5577 */
       
  5578 
       
  5579 const quint64 &QMailMessageMetaData::Incoming = incomingFlag;
       
  5580 const quint64 &QMailMessageMetaData::Outgoing = outgoingFlag;
       
  5581 const quint64 &QMailMessageMetaData::Sent = sentFlag;
       
  5582 const quint64 &QMailMessageMetaData::Replied = repliedFlag;
       
  5583 const quint64 &QMailMessageMetaData::RepliedAll = repliedAllFlag;
       
  5584 const quint64 &QMailMessageMetaData::Forwarded = forwardedFlag;
       
  5585 const quint64 &QMailMessageMetaData::ContentAvailable = contentAvailableFlag;
       
  5586 const quint64 &QMailMessageMetaData::Read = readFlag;
       
  5587 const quint64 &QMailMessageMetaData::Removed = removedFlag;
       
  5588 const quint64 &QMailMessageMetaData::ReadElsewhere = readElsewhereFlag;
       
  5589 const quint64 &QMailMessageMetaData::UnloadedData = unloadedDataFlag;
       
  5590 const quint64 &QMailMessageMetaData::New = newFlag;
       
  5591 const quint64 &QMailMessageMetaData::ReadReplyRequested = readReplyRequestedFlag;
       
  5592 const quint64 &QMailMessageMetaData::Trash = trashFlag;
       
  5593 const quint64 &QMailMessageMetaData::PartialContentAvailable = partialContentAvailableFlag;
       
  5594 const quint64 &QMailMessageMetaData::HasAttachments = hasAttachmentsFlag;
       
  5595 const quint64 &QMailMessageMetaData::HasReferences = hasReferencesFlag;
       
  5596 const quint64 &QMailMessageMetaData::HasUnresolvedReferences = hasUnresolvedReferencesFlag;
       
  5597 const quint64 &QMailMessageMetaData::Draft = draftFlag;
       
  5598 const quint64 &QMailMessageMetaData::Outbox = outboxFlag;
       
  5599 const quint64 &QMailMessageMetaData::Junk = junkFlag;
       
  5600 const quint64 &QMailMessageMetaData::TransmitFromExternal = transmitFromExternalFlag;
       
  5601 const quint64 &QMailMessageMetaData::LocalOnly = localOnlyFlag;
       
  5602 
       
  5603 /*!
       
  5604     Constructs an empty message meta data object.
       
  5605 */
       
  5606 QMailMessageMetaData::QMailMessageMetaData()
       
  5607     : QPrivatelyImplemented<QMailMessageMetaDataPrivate>(new QMailMessageMetaDataPrivate())
       
  5608 {
       
  5609 }
       
  5610 
       
  5611 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5612 /*!
       
  5613     Constructs a message meta data object from data stored in the message store with QMailMessageId \a id.
       
  5614 */
       
  5615 QMailMessageMetaData::QMailMessageMetaData(const QMailMessageId& id)
       
  5616     : QPrivatelyImplemented<QMailMessageMetaDataPrivate>(0)
       
  5617 {
       
  5618     *this = QMailStore::instance()->messageMetaData(id);
       
  5619 }
       
  5620 
       
  5621 /*!
       
  5622     Constructs a message meta data object from data stored in the message store with the unique 
       
  5623     identifier \a uid from the account with id \a accountId.
       
  5624 */
       
  5625 QMailMessageMetaData::QMailMessageMetaData(const QString& uid, const QMailAccountId& accountId)
       
  5626     : QPrivatelyImplemented<QMailMessageMetaDataPrivate>(0)
       
  5627 {
       
  5628     *this = QMailStore::instance()->messageMetaData(uid, accountId);
       
  5629 }
       
  5630 #endif
       
  5631 
       
  5632 /*!
       
  5633     Sets the MessageType of the message to \a type.
       
  5634 
       
  5635     \sa messageType()
       
  5636 */
       
  5637 void QMailMessageMetaData::setMessageType(QMailMessageMetaData::MessageType type)
       
  5638 {
       
  5639     switch (type) {
       
  5640         case QMailMessage::Mms:
       
  5641         case QMailMessage::Sms:
       
  5642         case QMailMessage::Email:
       
  5643         case QMailMessage::Instant:
       
  5644         case QMailMessage::System:
       
  5645             break;
       
  5646         default:
       
  5647             qWarning() << "QMailMessageMetaData::setMessageType:" << type;
       
  5648             return;
       
  5649     }
       
  5650 
       
  5651     impl(this)->setMessageType(type);
       
  5652 }
       
  5653 
       
  5654 /*!
       
  5655     Returns the MessageType of the message.
       
  5656 
       
  5657     \sa setMessageType()
       
  5658 */
       
  5659 QMailMessageMetaData::MessageType QMailMessageMetaData::messageType() const
       
  5660 {
       
  5661     return impl(this)->_messageType;
       
  5662 }
       
  5663 
       
  5664 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5665 /*!
       
  5666     Return the QMailFolderId of the folder that contains the message.
       
  5667 */
       
  5668 QMailFolderId QMailMessageMetaData::parentFolderId() const
       
  5669 {
       
  5670     return impl(this)->_parentFolderId;
       
  5671 }
       
  5672 
       
  5673 /*!
       
  5674     Sets the QMailFolderId of the folder that contains the message to \a id.
       
  5675 */
       
  5676 void QMailMessageMetaData::setParentFolderId(const QMailFolderId &id)
       
  5677 {
       
  5678     impl(this)->setParentFolderId(id);
       
  5679 }
       
  5680 #endif
       
  5681 
       
  5682 /*!
       
  5683     Returns the Qt Extended unique QMailMessageId of the message.
       
  5684 */
       
  5685 QMailMessageId QMailMessageMetaData::id() const
       
  5686 {
       
  5687     return impl(this)->_id;
       
  5688 }
       
  5689 
       
  5690 /*!
       
  5691     Sets the QMailMessageId of the message to \a id.
       
  5692     \a id should be different for each message known to Qtopia.
       
  5693 */
       
  5694 void QMailMessageMetaData::setId(const QMailMessageId &id)
       
  5695 {
       
  5696     impl(this)->setId(id);
       
  5697 }
       
  5698 
       
  5699 /*!
       
  5700     Returns the originating address of the message.
       
  5701 */
       
  5702 QMailAddress QMailMessageMetaData::from() const
       
  5703 {
       
  5704     return QMailAddress(impl(this)->_from);
       
  5705 }
       
  5706 
       
  5707 /*!
       
  5708     Sets the from address, that is the originating address of the message to \a from.
       
  5709 */
       
  5710 void QMailMessageMetaData::setFrom(const QMailAddress &from)
       
  5711 {
       
  5712     impl(this)->setFrom(from.toString());
       
  5713 }
       
  5714 
       
  5715 /*!
       
  5716     Returns the subject of the message, if present; otherwise returns an empty string.
       
  5717 */
       
  5718 QString QMailMessageMetaData::subject() const
       
  5719 {
       
  5720     return impl(this)->_subject;
       
  5721 }
       
  5722 
       
  5723 /*!
       
  5724     Sets the subject of the message to \a subject.
       
  5725 */
       
  5726 void QMailMessageMetaData::setSubject(const QString &subject)
       
  5727 {
       
  5728     impl(this)->setSubject(subject);
       
  5729 }
       
  5730 
       
  5731 
       
  5732 /*!
       
  5733     Returns the timestamp contained in the origination date header field of the message, if present; 
       
  5734     otherwise returns an empty timestamp.
       
  5735 */
       
  5736 QMailTimeStamp QMailMessageMetaData::date() const
       
  5737 {
       
  5738     return QMailTimeStamp(impl(this)->_date);
       
  5739 }
       
  5740 
       
  5741 /*!
       
  5742     Sets the origination date header field specifying the timestamp of the message to \a timeStamp.
       
  5743 */
       
  5744 void QMailMessageMetaData::setDate(const QMailTimeStamp &timeStamp)
       
  5745 {
       
  5746     impl(this)->setDate(timeStamp);
       
  5747 }
       
  5748 
       
  5749 /*!
       
  5750     Returns the timestamp placed in the message during reception by the messageserver, if present;
       
  5751     otherwise returns an empty timestamp.
       
  5752 */
       
  5753 QMailTimeStamp QMailMessageMetaData::receivedDate() const
       
  5754 {
       
  5755     return QMailTimeStamp(impl(this)->_receivedDate);
       
  5756 }
       
  5757 
       
  5758 /*!
       
  5759     Sets the timestamp indicating the time of message reception by the messageserver to \a timeStamp.
       
  5760 */
       
  5761 void QMailMessageMetaData::setReceivedDate(const QMailTimeStamp &timeStamp)
       
  5762 {
       
  5763     impl(this)->setReceivedDate(timeStamp);
       
  5764 }
       
  5765 
       
  5766 /*! 
       
  5767     Returns the list of primary recipients for the message.
       
  5768 
       
  5769     \sa QMailAddress
       
  5770 */
       
  5771 QList<QMailAddress> QMailMessageMetaData::to() const
       
  5772 {
       
  5773     return QMailAddress::fromStringList(impl(this)->_to);
       
  5774 }
       
  5775 
       
  5776 /*! 
       
  5777     Sets the list of primary recipients for the message to \a toList.
       
  5778 */
       
  5779 void QMailMessageMetaData::setTo(const QList<QMailAddress>& toList)
       
  5780 {
       
  5781     impl(this)->setTo(QMailAddress::toStringList(toList).join(","));
       
  5782 }
       
  5783 
       
  5784 /*! 
       
  5785     Sets the list of primary recipients for the message to contain \a address.
       
  5786 */
       
  5787 void QMailMessageMetaData::setTo(const QMailAddress& address)
       
  5788 {
       
  5789     setTo(QList<QMailAddress>() << address);
       
  5790 }
       
  5791 
       
  5792 /*!
       
  5793     Returns the status value for the message.
       
  5794 
       
  5795     \sa setStatus(), statusMask()
       
  5796 */  
       
  5797 quint64 QMailMessageMetaData::status() const
       
  5798 {
       
  5799     return impl(this)->_status;
       
  5800 }
       
  5801 
       
  5802 /*!
       
  5803     Sets the status value for the message to \a newStatus.
       
  5804 
       
  5805     \sa status(), statusMask()
       
  5806 */
       
  5807 void QMailMessageMetaData::setStatus(quint64 newStatus)
       
  5808 {
       
  5809     impl(this)->setStatus(newStatus);
       
  5810 }
       
  5811 
       
  5812 /*!
       
  5813     Sets the status flags indicated in \a mask to \a set.
       
  5814 
       
  5815     \sa status(), statusMask()
       
  5816 */
       
  5817 void QMailMessageMetaData::setStatus(quint64 mask, bool set)
       
  5818 {
       
  5819     quint64 newStatus = impl(this)->_status;
       
  5820 
       
  5821     if (set)
       
  5822         newStatus |= mask;
       
  5823     else
       
  5824         newStatus &= ~mask;
       
  5825     impl(this)->setStatus(newStatus);
       
  5826 }
       
  5827 
       
  5828 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5829 /*!
       
  5830     Returns the id of the originating account for the message.
       
  5831 */  
       
  5832 QMailAccountId QMailMessageMetaData::parentAccountId() const
       
  5833 {
       
  5834     return impl(this)->_parentAccountId;
       
  5835 }
       
  5836 
       
  5837 /*!
       
  5838     Sets the id of the originating account for the message to \a id.
       
  5839 */ 
       
  5840 void QMailMessageMetaData::setParentAccountId(const QMailAccountId& id)
       
  5841 {
       
  5842     impl(this)->setParentAccountId(id);
       
  5843 }
       
  5844 #endif
       
  5845 
       
  5846 /*!
       
  5847     Returns the identifier for the message on the originating server.
       
  5848 */  
       
  5849 QString QMailMessageMetaData::serverUid() const
       
  5850 {
       
  5851     return impl(this)->_serverUid;
       
  5852 }
       
  5853 
       
  5854 /*!
       
  5855     Sets the originating server identifier for the message to \a server.
       
  5856     The identifier specified should be unique.
       
  5857 */  
       
  5858 void QMailMessageMetaData::setServerUid(const QString &server)
       
  5859 {
       
  5860     impl(this)->setServerUid(server);
       
  5861 }
       
  5862 
       
  5863 /*!
       
  5864     Returns the complete size of the message as indicated on the originating server.
       
  5865 */  
       
  5866 uint QMailMessageMetaData::size() const
       
  5867 {
       
  5868     return impl(this)->_size;
       
  5869 }
       
  5870 
       
  5871 /*!
       
  5872     Sets the complete size of the message as found on the server to \a size.
       
  5873 */  
       
  5874 void QMailMessageMetaData::setSize(uint size)
       
  5875 {
       
  5876     impl(this)->setSize(size);
       
  5877 }
       
  5878 
       
  5879 /*!
       
  5880     Returns an indication of the size of the message.  This measure should be used
       
  5881     only in comparing the relative size of messages with respect to transmission.
       
  5882 */  
       
  5883 uint QMailMessageMetaData::indicativeSize() const
       
  5884 {
       
  5885     return impl(this)->indicativeSize();
       
  5886 }
       
  5887 
       
  5888 /*!
       
  5889     Returns the type of content contained within the message.
       
  5890 */  
       
  5891 QMailMessage::ContentType QMailMessageMetaData::content() const
       
  5892 {
       
  5893     return impl(this)->_contentType;
       
  5894 }
       
  5895 
       
  5896 /*!
       
  5897     \fn QMailMessageMetaData::setContent(QMailMessageMetaData::ContentType)
       
  5898 
       
  5899     Sets the type of content contained within the message to \a type.  
       
  5900     It is the caller's responsibility to ensure that this value matches the actual content.
       
  5901 */  
       
  5902 void QMailMessageMetaData::setContent(QMailMessage::ContentType type)
       
  5903 {
       
  5904     impl(this)->setContent(type);
       
  5905 }
       
  5906 
       
  5907 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  5908 /*!
       
  5909     Return the QMailFolderId of the folder that contained the message before it was 
       
  5910     moved into the current parent folder.
       
  5911 */
       
  5912 QMailFolderId QMailMessageMetaData::previousParentFolderId() const
       
  5913 {
       
  5914     return impl(this)->_previousParentFolderId;
       
  5915 }
       
  5916 
       
  5917 /*!
       
  5918     Sets the QMailFolderId of the folder that contained the message before it was 
       
  5919     moved into the current parent folder to \a id.
       
  5920 */
       
  5921 void QMailMessageMetaData::setPreviousParentFolderId(const QMailFolderId &id)
       
  5922 {
       
  5923     impl(this)->setPreviousParentFolderId(id);
       
  5924 }
       
  5925 #endif
       
  5926 
       
  5927 /*!
       
  5928     Returns the scheme used to store the content of this message.
       
  5929 */  
       
  5930 QString QMailMessageMetaData::contentScheme() const
       
  5931 {
       
  5932     return impl(this)->_contentScheme;
       
  5933 }
       
  5934 
       
  5935 /*!
       
  5936     Sets the scheme used to store the content of this message to \a scheme, and returns
       
  5937     true if successful.  Once set, the scheme cannot be modified.
       
  5938 */  
       
  5939 bool QMailMessageMetaData::setContentScheme(const QString &scheme)
       
  5940 {
       
  5941     if (!impl(this)->_contentScheme.isEmpty() && (impl(this)->_contentScheme != scheme)) {
       
  5942         qMailLog(Messaging) << "Warning - modifying existing content scheme from:" << impl(this)->_contentScheme << "to:" << scheme;
       
  5943     }
       
  5944 
       
  5945     impl(this)->setContentScheme(scheme);
       
  5946     return true;
       
  5947 }
       
  5948 
       
  5949 /*!
       
  5950     Returns the identifer used to locate the content of this message.
       
  5951 */  
       
  5952 QString QMailMessageMetaData::contentIdentifier() const
       
  5953 {
       
  5954     return impl(this)->_contentIdentifier;
       
  5955 }
       
  5956 
       
  5957 /*!
       
  5958     Sets the identifer used to locate the content of this message to \a identifier, and returns
       
  5959     true if successful.  Once set, the identifier cannot be modified.
       
  5960 
       
  5961     The identifier specified should be unique within the scheme returned by contentScheme().
       
  5962 */  
       
  5963 bool QMailMessageMetaData::setContentIdentifier(const QString &identifier)
       
  5964 {
       
  5965     impl(this)->setContentIdentifier(identifier);
       
  5966     return true;
       
  5967 }
       
  5968 
       
  5969 /*!
       
  5970     Returns the identifier of the message that this message was created in response to.
       
  5971 */  
       
  5972 QMailMessageId QMailMessageMetaData::inResponseTo() const
       
  5973 {
       
  5974     return impl(this)->_responseId;
       
  5975 }
       
  5976 
       
  5977 /*!
       
  5978     Sets the identifier of the message that this message was created in response to, to \a id.
       
  5979 */
       
  5980 void QMailMessageMetaData::setInResponseTo(const QMailMessageId &id)
       
  5981 {
       
  5982     impl(this)->setInResponseTo(id);
       
  5983 }
       
  5984 
       
  5985 /*!
       
  5986     Returns the type of response that this message was created as.
       
  5987 
       
  5988     \sa inResponseTo()
       
  5989 */  
       
  5990 QMailMessageMetaData::ResponseType QMailMessageMetaData::responseType() const
       
  5991 {
       
  5992     return impl(this)->_responseType;
       
  5993 }
       
  5994 
       
  5995 /*!
       
  5996     Sets the type of response that this message was created as to \a type.
       
  5997 
       
  5998     \sa setInResponseTo()
       
  5999 */
       
  6000 void QMailMessageMetaData::setResponseType(QMailMessageMetaData::ResponseType type)
       
  6001 {
       
  6002     impl(this)->setResponseType(type);
       
  6003 }
       
  6004 
       
  6005 /*!
       
  6006     Returns true if the entire content of this message is available; otherwise returns false.
       
  6007 */
       
  6008 bool QMailMessageMetaData::contentAvailable() const
       
  6009 {
       
  6010     return (status() & QMailMessage::ContentAvailable);
       
  6011 }
       
  6012 
       
  6013 /*!
       
  6014     Returns true if some portion of the content of this message is available; otherwise returns false.
       
  6015 */
       
  6016 bool QMailMessageMetaData::partialContentAvailable() const
       
  6017 {
       
  6018     return (status() & QMailMessage::PartialContentAvailable);
       
  6019 }
       
  6020 
       
  6021 /*! \internal */
       
  6022 bool QMailMessageMetaData::dataModified() const
       
  6023 {
       
  6024     return impl(this)->dataModified();
       
  6025 }
       
  6026 
       
  6027 /*! \internal */
       
  6028 void QMailMessageMetaData::setUnmodified()
       
  6029 {
       
  6030     impl(this)->setUnmodified();
       
  6031 }
       
  6032 
       
  6033 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  6034 /*!
       
  6035     Returns the status bitmask needed to test the result of QMailMessageMetaData::status() 
       
  6036     against the QMailMessageMetaData status flag registered with the identifier \a flagName.
       
  6037 
       
  6038     \sa status(), QMailStore::messageStatusMask()
       
  6039 */
       
  6040 quint64 QMailMessageMetaData::statusMask(const QString &flagName)
       
  6041 {
       
  6042     return QMailStore::instance()->messageStatusMask(flagName);
       
  6043 }
       
  6044 #endif
       
  6045 
       
  6046 /*! 
       
  6047     Returns the value recorded in the custom field named \a name.
       
  6048 
       
  6049     \sa setCustomField(), customFields()
       
  6050 */
       
  6051 QString QMailMessageMetaData::customField(const QString &name) const
       
  6052 {
       
  6053     return d->customField(name);
       
  6054 }
       
  6055 
       
  6056 /*! 
       
  6057     Sets the value of the custom field named \a name to \a value.
       
  6058 
       
  6059     \sa customField(), customFields()
       
  6060 */
       
  6061 void QMailMessageMetaData::setCustomField(const QString &name, const QString &value)
       
  6062 {
       
  6063     d->setCustomField(name, value);
       
  6064 }
       
  6065 
       
  6066 /*! 
       
  6067     Removes the custom field named \a name.
       
  6068 
       
  6069     \sa customField(), customFields()
       
  6070 */
       
  6071 void QMailMessageMetaData::removeCustomField(const QString &name)
       
  6072 {
       
  6073     d->removeCustomField(name);
       
  6074 }
       
  6075 
       
  6076 /*! 
       
  6077     Returns the map of custom fields stored in the message.
       
  6078 
       
  6079     \sa customField(), setCustomField()
       
  6080 */
       
  6081 const QMap<QString, QString> &QMailMessageMetaData::customFields() const
       
  6082 {
       
  6083     return d->_customFields;
       
  6084 }
       
  6085 
       
  6086 /*! \internal */
       
  6087 void QMailMessageMetaData::setCustomFields(const QMap<QString, QString> &fields)
       
  6088 {
       
  6089     d->setCustomFields(fields);
       
  6090 }
       
  6091 
       
  6092 /*! \internal */
       
  6093 bool QMailMessageMetaData::customFieldsModified() const
       
  6094 {
       
  6095     return d->_customFieldsModified;
       
  6096 }
       
  6097 
       
  6098 /*! \internal */
       
  6099 void QMailMessageMetaData::setCustomFieldsModified(bool set)
       
  6100 {
       
  6101     d->_customFieldsModified = set;
       
  6102 }
       
  6103 
       
  6104 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  6105 /*! \internal */
       
  6106 void QMailMessageMetaData::initStore()
       
  6107 {
       
  6108     QMailMessageMetaDataPrivate::initializeFlags();
       
  6109 }
       
  6110 #endif
       
  6111 
       
  6112 /*! 
       
  6113     \fn QMailMessageMetaData::serialize(Stream&) const
       
  6114     \internal 
       
  6115 */
       
  6116 template <typename Stream> 
       
  6117 void QMailMessageMetaData::serialize(Stream &stream) const
       
  6118 {
       
  6119     impl(this)->serialize(stream);
       
  6120 }
       
  6121 
       
  6122 template void QMailMessageMetaData::serialize(QDataStream &) const;
       
  6123 
       
  6124 /*! 
       
  6125     \fn QMailMessageMetaData::deserialize(Stream&)
       
  6126     \internal 
       
  6127 */
       
  6128 template <typename Stream> 
       
  6129 void QMailMessageMetaData::deserialize(Stream &stream)
       
  6130 {
       
  6131     impl(this)->deserialize(stream);
       
  6132 }
       
  6133 
       
  6134 template void QMailMessageMetaData::deserialize(QDataStream &);
       
  6135 
       
  6136 
       
  6137 /*  QMailMessage */
       
  6138 
       
  6139 QMailMessagePrivate::QMailMessagePrivate()
       
  6140     : QMailMessagePartContainerPrivate(this)
       
  6141 {
       
  6142 }
       
  6143 
       
  6144 void QMailMessagePrivate::fromRfc2822(const LongString &ls)
       
  6145 {
       
  6146     _messageParts.clear();
       
  6147 
       
  6148     if (ls.length()) {
       
  6149         QMailMessageContentType contentType(headerField("Content-Type"));
       
  6150 
       
  6151         // Is this a simple mail or a multi-part collection?
       
  6152         QByteArray mimeVersion = headerField("MIME-Version");
       
  6153         QByteArray minimalVersion = QMailMessageHeaderField::removeWhitespace(QMailMessageHeaderField::removeComments(mimeVersion));
       
  6154         if (!mimeVersion.isEmpty() && (minimalVersion != "1.0")) {
       
  6155             qWarning() << "Unknown MIME-Version:" << mimeVersion;
       
  6156         } else if (_multipartType != QMailMessagePartContainer::MultipartNone) {
       
  6157             parseMimeMultipart(_header, ls, true);
       
  6158         } else {
       
  6159             QByteArray bodyData;
       
  6160 
       
  6161             // Remove the pop-style terminator if present
       
  6162             const QByteArray popTerminator((QByteArray(QMailMessage::CRLF) + '.' + QMailMessage::CRLF));
       
  6163             if ( ls.indexOf(popTerminator, -popTerminator.length()) != -1)
       
  6164                 bodyData = ls.left( ls.length() - popTerminator.length() ).toQByteArray();
       
  6165             else
       
  6166                 bodyData = ls.toQByteArray();
       
  6167 
       
  6168             // The body data is already encoded
       
  6169             QDataStream in(bodyData);
       
  6170             QMailMessageBody::TransferEncoding encoding = encodingForName(headerField("Content-Transfer-Encoding"));
       
  6171             if ( encoding == QMailMessageBody::NoEncoding )
       
  6172                 encoding = QMailMessageBody::SevenBit;
       
  6173 
       
  6174             setBody( QMailMessageBody::fromStream(in, contentType, encoding, QMailMessageBody::AlreadyEncoded) );
       
  6175         }
       
  6176     }
       
  6177 }
       
  6178 
       
  6179 void QMailMessagePrivate::setId(const QMailMessageId& id)
       
  6180 {
       
  6181     setLocation(id, _indices);
       
  6182 }
       
  6183 
       
  6184 void QMailMessagePrivate::setSubject(const QString& s)
       
  6185 {
       
  6186     updateHeaderField( "Subject:", s );
       
  6187 }
       
  6188 
       
  6189 void QMailMessagePrivate::setDate(const QMailTimeStamp& timeStamp)
       
  6190 {
       
  6191     updateHeaderField( "Date:", to7BitAscii(timeStamp.toString()) );
       
  6192 }
       
  6193 
       
  6194 void QMailMessagePrivate::setFrom(const QString& s)
       
  6195 {
       
  6196     updateHeaderField( "From:", s );
       
  6197 } 
       
  6198 
       
  6199 void QMailMessagePrivate::setReplyTo(const QString& s)
       
  6200 {
       
  6201     updateHeaderField( "Reply-To:", s );
       
  6202 }
       
  6203 
       
  6204 void QMailMessagePrivate::setInReplyTo(const QString& s)
       
  6205 {
       
  6206     updateHeaderField( "In-Reply-To:", s );
       
  6207 }
       
  6208 
       
  6209 void QMailMessagePrivate::setTo(const QString& s)
       
  6210 {
       
  6211     updateHeaderField( "To:", s );
       
  6212 }
       
  6213 
       
  6214 void QMailMessagePrivate::setBcc(const QString& s)
       
  6215 {
       
  6216     updateHeaderField( "Bcc:", s );
       
  6217 }
       
  6218 
       
  6219 void QMailMessagePrivate::setCc(const QString& s)
       
  6220 {
       
  6221     updateHeaderField( "Cc:", s );
       
  6222 }
       
  6223 
       
  6224 bool QMailMessagePrivate::hasRecipients() const
       
  6225 {
       
  6226     if ( !headerField("To").isEmpty() )
       
  6227         return true;
       
  6228     if ( !headerField("Cc").isEmpty() )
       
  6229         return true;
       
  6230     if ( !headerField("Bcc").isEmpty() )
       
  6231         return true;
       
  6232 
       
  6233     return false;
       
  6234 }
       
  6235 
       
  6236 uint QMailMessagePrivate::indicativeSize() const
       
  6237 {
       
  6238     uint size = QMailMessagePartContainerPrivate::indicativeSize(); 
       
  6239 
       
  6240     // Count the message header as one size unit
       
  6241     return (size + 1);
       
  6242 }
       
  6243 
       
  6244 static uint currentTimeValue()
       
  6245 {
       
  6246     return QDateTime::currentDateTime().toTime_t();
       
  6247 }
       
  6248 
       
  6249 static bool seedRng()
       
  6250 {
       
  6251     qsrand(currentTimeValue());
       
  6252     return true;
       
  6253 }
       
  6254 
       
  6255 static int randomNumber()
       
  6256 {
       
  6257     static bool initialised = seedRng();
       
  6258     return qrand();
       
  6259 
       
  6260     Q_UNUSED(initialised)
       
  6261 }
       
  6262 
       
  6263 static QByteArray gBoundaryString;
       
  6264 
       
  6265 void QTOPIAMAIL_EXPORT setQMailMessageBoundaryString(const QByteArray &boundary)
       
  6266 {
       
  6267     gBoundaryString = boundary;
       
  6268 }
       
  6269 
       
  6270 static QByteArray boundaryString(const QByteArray &hash)
       
  6271 {
       
  6272     static const QByteArray boundaryLeader = "[)}<";
       
  6273     static const QByteArray boundaryTrailer = ")}<]";
       
  6274 
       
  6275     if (!gBoundaryString.isEmpty())
       
  6276         return gBoundaryString;
       
  6277 
       
  6278     // Formulate a boundary that is very unlikely to clash with the content
       
  6279     return boundaryLeader + "qtopiamail:" + QByteArray::number(randomNumber()) + hash.toBase64() + boundaryTrailer;
       
  6280 }
       
  6281 
       
  6282 template <typename F>
       
  6283 void QMailMessagePrivate::toRfc2822(QDataStream **out, QMailMessage::EncodingFormat format, quint64 messageStatus, F *func) const
       
  6284 {
       
  6285     bool isOutgoing = (messageStatus & (QMailMessage::Outgoing | QMailMessage::Sent));
       
  6286 
       
  6287     bool addTimeStamp = (format != QMailMessage::IdentityFormat);
       
  6288     bool addContentHeaders = ((format != QMailMessage::IdentityFormat) && 
       
  6289                               ((format != QMailMessage::StorageFormat) || isOutgoing || !hasBody()));
       
  6290     bool includeBcc = (format != QMailMessage::TransmissionFormat);
       
  6291     bool excludeInternalFields = (format == QMailMessage::TransmissionFormat);
       
  6292 
       
  6293     if (_messageParts.count() && boundary().isEmpty()) {
       
  6294         // Include a hash of the header data in the boundary
       
  6295         QCryptographicHash hash(QCryptographicHash::Md5);
       
  6296         foreach (const QByteArray* field, _header.fieldList())
       
  6297             hash.addData(*field);
       
  6298 
       
  6299         const_cast<QMailMessagePrivate*>(this)->setBoundary(boundaryString(hash.result()));
       
  6300     }
       
  6301 
       
  6302     outputHeaders(**out, addTimeStamp, addContentHeaders, includeBcc, excludeInternalFields);
       
  6303     **out << DataString('\n');
       
  6304 
       
  6305     if (format != QMailMessage::HeaderOnlyFormat) {
       
  6306         if ( hasBody() ) {
       
  6307             outputBody( **out, true); //not multipart so part should not be an attachment
       
  6308         } else {
       
  6309             bool addMimePreamble = (format == QMailMessage::TransmissionFormat);
       
  6310             bool includeAttachments = (format != QMailMessage::StorageFormat);
       
  6311 
       
  6312             outputParts<F>( out, addMimePreamble, includeAttachments, excludeInternalFields, func );
       
  6313         }
       
  6314     }
       
  6315 }
       
  6316 
       
  6317 void QMailMessagePrivate::outputHeaders( QDataStream& out, bool addTimeStamp, bool addContentHeaders, bool includeBcc, bool excludeInternalFields ) const
       
  6318 {
       
  6319     QList<QByteArray> exclusions;
       
  6320 
       
  6321     if (addContentHeaders) {
       
  6322         // Don't include the nominated MIME-Version if specified - we implement 1.0
       
  6323         exclusions.append("MIME-Version");
       
  6324     }
       
  6325     if (!includeBcc) {
       
  6326         exclusions.append("bcc");
       
  6327     }
       
  6328 
       
  6329     _header.output( out, exclusions, excludeInternalFields );
       
  6330 
       
  6331     if (addTimeStamp && headerField("Date").isEmpty()) {
       
  6332         QString timeStamp = QMailTimeStamp( QDateTime::currentDateTime() ).toString();
       
  6333         out << DataString("Date: ") << DataString(to7BitAscii(timeStamp)) << DataString('\n');
       
  6334     }
       
  6335 
       
  6336     if (addContentHeaders) {
       
  6337         // Output required content header fields
       
  6338         out << DataString("MIME-Version: 1.0") << DataString('\n');
       
  6339     }
       
  6340 }
       
  6341 
       
  6342 bool QMailMessagePrivate::contentModified() const
       
  6343 {
       
  6344     // For this part of any sub-part
       
  6345     return dirty(true);
       
  6346 }
       
  6347 
       
  6348 void QMailMessagePrivate::setUnmodified()
       
  6349 {
       
  6350     setDirty(false, true);
       
  6351 }
       
  6352 
       
  6353 template <typename Stream> 
       
  6354 void QMailMessagePrivate::serialize(Stream &stream) const
       
  6355 {
       
  6356     QMailMessagePartContainerPrivate::serialize(stream);
       
  6357 }
       
  6358 
       
  6359 template <typename Stream> 
       
  6360 void QMailMessagePrivate::deserialize(Stream &stream)
       
  6361 {
       
  6362     QMailMessagePartContainerPrivate::deserialize(stream);
       
  6363 }
       
  6364 
       
  6365 
       
  6366 //===========================================================================
       
  6367 
       
  6368 /*!
       
  6369     \class QMailMessage
       
  6370 
       
  6371     \brief The QMailMessage class provides a convenient interface for working with messages.
       
  6372     
       
  6373     \ingroup messaginglibrary
       
  6374    
       
  6375     QMailMessage supports a number of types. These include telephony types 
       
  6376     such as SMS and MMS, and internet email messages as defined in 
       
  6377     \l{http://www.ietf.org/rfc/rfc2822.txt} {RFC 2822} (Internet Message Format), and 
       
  6378     \l{http://www.ietf.org/rfc/rfc2045.txt} {RFC 2045} (Format of Internet Message Bodies) through 
       
  6379     \l{http://www.ietf.org/rfc/rfc2049.txt} {RFC 2049} (Conformance Criteria and Examples).
       
  6380     
       
  6381     The most common way to use QMailMessage is to initialize it from raw
       
  6382     data using QMailMessage::fromRfc2822().
       
  6383     
       
  6384     A QMailMessage can also be constructed piece by piece using functions such as 
       
  6385     setMessageType(), setFrom(), setTo(), setSubject(), and setBody() or appendPart(). 
       
  6386     Convenience functions such as from()/setFrom() and date()/setDate() accept and
       
  6387     return wrapper types, to simplify the exchange of correctly-formatted data.
       
  6388     In some cases, however, it may be simpler for clients to get and set the content 
       
  6389     of header fields directly, using the headerField() and setHeaderField() functions inherited
       
  6390     from QMailMessagePartContainer.
       
  6391     
       
  6392     Messages can be added to the QMailStore, or retrieved from the store via their QMailMessageId 
       
  6393     identifier.  The QMailMessage object also provides acces to any relevant meta data
       
  6394     describing the message, using the functions inherited from QMailMessageMetaData.
       
  6395 
       
  6396     A message may be serialized to a QDataStream, or returned as a QByteArray using toRfc2822().
       
  6397     
       
  6398     \sa QMailMessageMetaData, QMailMessagePart, QMailMessageBody, QMailStore, QMailMessageId
       
  6399 */    
       
  6400 
       
  6401 
       
  6402 const char QMailMessage::CarriageReturn = '\015';
       
  6403 const char QMailMessage::LineFeed = '\012';
       
  6404 const char* QMailMessage::CRLF = "\015\012";
       
  6405 
       
  6406 /*!
       
  6407     Constructs an empty message object.
       
  6408 */
       
  6409 QMailMessage::QMailMessage()
       
  6410     : QMailMessageMetaData(),
       
  6411       QMailMessagePartContainer(new QMailMessagePrivate())
       
  6412 {
       
  6413 }
       
  6414 
       
  6415 #ifndef QTOPIAMAIL_PARSING_ONLY
       
  6416 /*!
       
  6417     Constructs a message object from data stored in the message store with QMailMessageId \a id.
       
  6418 */
       
  6419 QMailMessage::QMailMessage(const QMailMessageId& id)
       
  6420     : QMailMessageMetaData(id),
       
  6421       QMailMessagePartContainer(reinterpret_cast<QMailMessagePrivate*>(0))
       
  6422 {
       
  6423     *this = QMailStore::instance()->message(id);
       
  6424 }
       
  6425 
       
  6426 /*!
       
  6427     Constructs a message object from data stored in the message store with the unique 
       
  6428     identifier \a uid from the account with id \a accountId.
       
  6429 */
       
  6430 QMailMessage::QMailMessage(const QString& uid, const QMailAccountId& accountId)
       
  6431     : QMailMessageMetaData(uid, accountId),
       
  6432       QMailMessagePartContainer(reinterpret_cast<QMailMessagePrivate*>(0))
       
  6433 {
       
  6434     *this = QMailStore::instance()->message(uid, accountId);
       
  6435 }
       
  6436 #endif
       
  6437 
       
  6438 /*!
       
  6439     Constructs a mail message from the RFC 2822 data contained in \a byteArray.
       
  6440 */
       
  6441 QMailMessage QMailMessage::fromRfc2822(const QByteArray &byteArray)
       
  6442 {
       
  6443     LongString ls(byteArray);
       
  6444     return fromRfc2822(ls);
       
  6445 }
       
  6446 
       
  6447 /*!
       
  6448     Constructs a mail message from the RFC 2822 data contained in the file \a fileName.
       
  6449 */
       
  6450 QMailMessage QMailMessage::fromRfc2822File(const QString& fileName)
       
  6451 {
       
  6452     LongString ls(fileName);
       
  6453     return fromRfc2822(ls);
       
  6454 }
       
  6455 
       
  6456 /*!
       
  6457     Returns true if the message contains a part with the location \a location.
       
  6458 */
       
  6459 bool QMailMessage::contains(const QMailMessagePart::Location& location) const
       
  6460 {
       
  6461     return partContainerImpl()->contains(location);
       
  6462 }
       
  6463 
       
  6464 /*!
       
  6465     Returns a const reference to the part at the location \a location within the message.
       
  6466 */
       
  6467 const QMailMessagePart& QMailMessage::partAt(const QMailMessagePart::Location& location) const
       
  6468 {
       
  6469     return partContainerImpl()->partAt(location);
       
  6470 }
       
  6471 
       
  6472 /*!
       
  6473     Returns a non-const reference to the part at the location \a location within the message.
       
  6474 */
       
  6475 QMailMessagePart& QMailMessage::partAt(const QMailMessagePart::Location& location)
       
  6476 {
       
  6477     return partContainerImpl()->partAt(location);
       
  6478 }
       
  6479 
       
  6480 /*! \reimp */
       
  6481 void QMailMessage::setHeaderField( const QString& id, const QString& value )
       
  6482 {
       
  6483     QMailMessagePartContainer::setHeaderField(id, value);
       
  6484 
       
  6485     QByteArray duplicatedId(duplicatedData(id));
       
  6486     if (!duplicatedId.isNull()) {
       
  6487         updateMetaData(duplicatedId, value);
       
  6488     }
       
  6489 }
       
  6490 
       
  6491 /*! \reimp */
       
  6492 void QMailMessage::setHeaderField( const QMailMessageHeaderField& field )
       
  6493 {
       
  6494     setHeaderField(field.id(), field.toString(false, false));
       
  6495 }
       
  6496 
       
  6497 /*! \reimp */
       
  6498 void QMailMessage::appendHeaderField( const QString& id, const QString& value )
       
  6499 {
       
  6500     QMailMessagePartContainer::appendHeaderField(id, value);
       
  6501 
       
  6502     QByteArray duplicatedId(duplicatedData(id));
       
  6503     if (!duplicatedId.isNull()) {
       
  6504         // We need to keep the value of the first item with this ID in the meta data object
       
  6505         updateMetaData(duplicatedId, headerFieldText(duplicatedId));
       
  6506     }
       
  6507 }
       
  6508 
       
  6509 /*! \reimp */
       
  6510 void QMailMessage::appendHeaderField( const QMailMessageHeaderField& field )
       
  6511 {
       
  6512     appendHeaderField(field.id(), field.toString(false, false));
       
  6513 }
       
  6514 
       
  6515 /*! \reimp */
       
  6516 void QMailMessage::removeHeaderField( const QString& id )
       
  6517 {
       
  6518     QMailMessagePartContainer::removeHeaderField(id);
       
  6519 
       
  6520     QByteArray duplicatedId(duplicatedData(id));
       
  6521     if (!duplicatedId.isNull()) {
       
  6522         updateMetaData(duplicatedId, QString());
       
  6523     }
       
  6524 }
       
  6525 
       
  6526 /*!
       
  6527     Returns the message in RFC 2822 format. The encoded content will vary depending on the value of \a format.
       
  6528 */
       
  6529 QByteArray QMailMessage::toRfc2822(EncodingFormat format) const
       
  6530 {
       
  6531     QByteArray result;
       
  6532     {
       
  6533         QDataStream out(&result, QIODevice::WriteOnly);
       
  6534         toRfc2822(out, format);
       
  6535     }
       
  6536     return result;
       
  6537 }
       
  6538 
       
  6539 /*!
       
  6540     Writes the message to the output stream \a out, in RFC 2822 format. 
       
  6541     The encoded content will vary depending on the value of \a format.
       
  6542 */
       
  6543 void QMailMessage::toRfc2822(QDataStream& out, EncodingFormat format) const
       
  6544 {
       
  6545     QDataStream *ds(&out);
       
  6546     partContainerImpl()->toRfc2822<DummyChunkProcessor>(&ds, format, status(), 0);
       
  6547 }
       
  6548 
       
  6549 struct ChunkStore
       
  6550 {
       
  6551     QList<QMailMessage::MessageChunk> chunks;
       
  6552     QByteArray chunk;
       
  6553     QDataStream *ds;
       
  6554 
       
  6555     ChunkStore()
       
  6556         : ds(new QDataStream(&chunk, QIODevice::WriteOnly | QIODevice::Unbuffered))
       
  6557     {
       
  6558     }
       
  6559 
       
  6560     ~ChunkStore()
       
  6561     {
       
  6562         close();
       
  6563     }
       
  6564 
       
  6565     void close() 
       
  6566     {
       
  6567         if (ds) {
       
  6568             delete ds;
       
  6569             ds = 0;
       
  6570 
       
  6571             if (!chunk.isEmpty()) {
       
  6572                 chunks.append(qMakePair(QMailMessage::Text, chunk));
       
  6573             }
       
  6574         }
       
  6575     }
       
  6576 
       
  6577     void operator()(QMailMessage::ChunkType type)
       
  6578     {
       
  6579         // This chunk is now complete
       
  6580         delete ds;
       
  6581         chunks.append(qMakePair(type, chunk));
       
  6582 
       
  6583         chunk.clear();
       
  6584         ds = new QDataStream(&chunk, QIODevice::WriteOnly | QIODevice::Unbuffered);
       
  6585     }
       
  6586 };
       
  6587 
       
  6588 /*!
       
  6589     Returns the message in RFC 2822 format, separated into chunks that can
       
  6590     be individually transferred by a mechanism such as that defined by RFC 3030.
       
  6591     The encoded content will vary depending on the value of \a format.
       
  6592 */
       
  6593 QList<QMailMessage::MessageChunk> QMailMessage::toRfc2822Chunks(EncodingFormat format) const
       
  6594 {
       
  6595     ChunkStore store;
       
  6596 
       
  6597     partContainerImpl()->toRfc2822<ChunkStore>(&store.ds, format, status(), &store);
       
  6598     store.close();
       
  6599 
       
  6600     return store.chunks;
       
  6601 }
       
  6602 
       
  6603 /*! \reimp */
       
  6604 void QMailMessage::setId(const QMailMessageId &id)
       
  6605 {
       
  6606     metaDataImpl()->setId(id);
       
  6607     partContainerImpl()->setId(id);
       
  6608 }
       
  6609 
       
  6610 /*! \reimp */
       
  6611 void QMailMessage::setFrom(const QMailAddress &from)
       
  6612 {
       
  6613     metaDataImpl()->setFrom(from.toString());
       
  6614     partContainerImpl()->setFrom(from.toString());
       
  6615 }
       
  6616 
       
  6617 /*! \reimp */
       
  6618 void QMailMessage::setSubject(const QString &subject)
       
  6619 {
       
  6620     metaDataImpl()->setSubject(subject);
       
  6621     partContainerImpl()->setSubject(subject);
       
  6622 }
       
  6623 
       
  6624 /*! \reimp */
       
  6625 void QMailMessage::setDate(const QMailTimeStamp &timeStamp)
       
  6626 {
       
  6627     metaDataImpl()->setDate(timeStamp);
       
  6628     partContainerImpl()->setDate(timeStamp);
       
  6629 }
       
  6630 
       
  6631 /*! \reimp */
       
  6632 void QMailMessage::setTo(const QList<QMailAddress>& toList)
       
  6633 {
       
  6634     QString flattened(QMailAddress::toStringList(toList).join(","));
       
  6635     metaDataImpl()->setTo(flattened);
       
  6636     partContainerImpl()->setTo(flattened);
       
  6637 }
       
  6638 
       
  6639 /*! \reimp */
       
  6640 void QMailMessage::setTo(const QMailAddress& address)
       
  6641 {
       
  6642     setTo(QList<QMailAddress>() << address);
       
  6643 }
       
  6644 
       
  6645 /*!
       
  6646     Returns a list of all the cc (carbon copy) recipients specified for the message.
       
  6647 
       
  6648     \sa to(), bcc(), QMailAddress
       
  6649 */  
       
  6650 QList<QMailAddress> QMailMessage::cc() const
       
  6651 {
       
  6652     return QMailAddress::fromStringList(headerFieldText("Cc"));
       
  6653 }
       
  6654 
       
  6655 /*!
       
  6656     Set the list of cc (carbon copy) recipients for the message to \a ccList.
       
  6657 
       
  6658     \sa setTo(), setBcc()
       
  6659 */  
       
  6660 void QMailMessage::setCc(const QList<QMailAddress>& ccList)
       
  6661 {
       
  6662     partContainerImpl()->setCc(QMailAddress::toStringList(ccList).join(","));
       
  6663 }
       
  6664 
       
  6665 /*!
       
  6666     Returns a list of all the bcc (blind carbon copy) recipients specified for the message.
       
  6667 
       
  6668     \sa to(), cc(), QMailAddress
       
  6669 */  
       
  6670 QList<QMailAddress> QMailMessage::bcc() const
       
  6671 {
       
  6672     return QMailAddress::fromStringList(headerFieldText("Bcc"));
       
  6673 }
       
  6674 
       
  6675 /*!
       
  6676     Set the list of bcc (blind carbon copy) recipients for the message to \a bccList.
       
  6677 
       
  6678     \sa setTo(), setCc()
       
  6679 */  
       
  6680 void QMailMessage::setBcc(const QList<QMailAddress>& bccList)
       
  6681 {
       
  6682     partContainerImpl()->setBcc(QMailAddress::toStringList(bccList).join(","));
       
  6683 }
       
  6684 
       
  6685 /*!
       
  6686     Returns the address specified by the RFC 2822 'Reply-To' field for the message, if present.
       
  6687 */  
       
  6688 QMailAddress QMailMessage::replyTo() const
       
  6689 {
       
  6690     return QMailAddress(headerFieldText("Reply-To"));
       
  6691 }
       
  6692 
       
  6693 /*!
       
  6694     Sets the RFC 2822 'Reply-To' address of the message to \a address.
       
  6695 */  
       
  6696 void QMailMessage::setReplyTo(const QMailAddress &address)
       
  6697 {
       
  6698     partContainerImpl()->setReplyTo(address.toString());
       
  6699 }
       
  6700 
       
  6701 /*!
       
  6702     Returns the message ID specified by the RFC 2822 'In-Reply-To' field for the message, if present.
       
  6703 */  
       
  6704 QString QMailMessage::inReplyTo() const
       
  6705 {
       
  6706     return headerFieldText("In-Reply-To");
       
  6707 }
       
  6708 
       
  6709 /*!
       
  6710     Sets the RFC 2822 'In-Reply-To' field for the message to \a messageId.
       
  6711 */
       
  6712 void QMailMessage::setInReplyTo(const QString &messageId)
       
  6713 {
       
  6714     partContainerImpl()->setInReplyTo(messageId);
       
  6715 }
       
  6716 
       
  6717 /*!
       
  6718     Returns a list of all the recipients specified for the message, either as To, CC, or BCC addresses.
       
  6719 
       
  6720     \sa to(), cc(), bcc(), hasRecipients()
       
  6721 */  
       
  6722 QList<QMailAddress> QMailMessage::recipients() const
       
  6723 {
       
  6724     QList<QMailAddress> addresses;
       
  6725 
       
  6726     QStringList list;
       
  6727     list.append( headerFieldText("To").trimmed() );
       
  6728     list.append( headerFieldText("Cc").trimmed() );
       
  6729     list.append( headerFieldText("Bcc").trimmed() );
       
  6730     if (!list.isEmpty()) {
       
  6731         list.removeAll( "" );
       
  6732         list.removeAll( QString::null );
       
  6733     }
       
  6734     if (!list.isEmpty()) {
       
  6735         addresses += QMailAddress::fromStringList( list.join(",") );
       
  6736     }
       
  6737 
       
  6738     return addresses;
       
  6739 }
       
  6740 
       
  6741 /*!
       
  6742     Returns true if there are any recipients (either To, CC or BCC addresses) 
       
  6743     defined for this message; otherwise returns false.
       
  6744 */  
       
  6745 bool QMailMessage::hasRecipients() const
       
  6746 {
       
  6747     return partContainerImpl()->hasRecipients();
       
  6748 }
       
  6749 
       
  6750 /*! \reimp */  
       
  6751 uint QMailMessage::indicativeSize() const
       
  6752 {
       
  6753     // Count the message header as one size unit
       
  6754     return partContainerImpl()->indicativeSize() + 1;
       
  6755 }
       
  6756 
       
  6757 /*!
       
  6758     Returns the size of the message content excluding any meta data, in bytes.
       
  6759 */  
       
  6760 uint QMailMessage::contentSize() const
       
  6761 {
       
  6762     return customField("qtopiamail-content-size").toUInt();
       
  6763 }
       
  6764 
       
  6765 /*!
       
  6766     Sets the size of the message content excluding any meta data to \a size, in bytes.
       
  6767 */
       
  6768 void QMailMessage::setContentSize(uint size)
       
  6769 {
       
  6770     setCustomField("qtopiamail-content-size", QString::number(size));
       
  6771 }
       
  6772 
       
  6773 /*!
       
  6774     Returns a value by which the external location of the message can be referenced, if available.
       
  6775 */  
       
  6776 QString QMailMessage::externalLocationReference() const
       
  6777 {
       
  6778     return customField("qtopiamail-external-location-reference");
       
  6779 }
       
  6780 
       
  6781 /*!
       
  6782     Returns the value by which the external location of the message can be referenced to \a location.
       
  6783 */
       
  6784 void QMailMessage::setExternalLocationReference(const QString &location)
       
  6785 {
       
  6786     setCustomField("qtopiamail-external-location-reference", location);
       
  6787 }
       
  6788 
       
  6789 /*! \reimp */
       
  6790 bool QMailMessage::contentAvailable() const
       
  6791 {
       
  6792     return QMailMessageMetaData::contentAvailable();
       
  6793 }
       
  6794 
       
  6795 /*! \reimp */
       
  6796 bool QMailMessage::partialContentAvailable() const
       
  6797 {
       
  6798     return QMailMessageMetaData::partialContentAvailable();
       
  6799 }
       
  6800 
       
  6801 // The QMMMetaData half of this object is implemented in a QMailMessageMetaDataPrivate object
       
  6802 /*! \internal */
       
  6803 QMailMessageMetaDataPrivate* QMailMessage::metaDataImpl() 
       
  6804 { 
       
  6805     return QMailMessageMetaData::impl<QMailMessageMetaDataPrivate>(); 
       
  6806 }
       
  6807 
       
  6808 /*! \internal */
       
  6809 const QMailMessageMetaDataPrivate* QMailMessage::metaDataImpl() const 
       
  6810 { 
       
  6811     return QMailMessageMetaData::impl<const QMailMessageMetaDataPrivate>(); 
       
  6812 }
       
  6813 
       
  6814 // The QMMPartContainer half of this object is implemented in a QMailMessagePrivate object
       
  6815 /*! \internal */
       
  6816 QMailMessagePrivate* QMailMessage::partContainerImpl() 
       
  6817 { 
       
  6818     return QMailMessagePartContainer::impl<QMailMessagePrivate>(); 
       
  6819 }
       
  6820 
       
  6821 /*! \internal */
       
  6822 const QMailMessagePrivate* QMailMessage::partContainerImpl() const 
       
  6823 { 
       
  6824     return QMailMessagePartContainer::impl<const QMailMessagePrivate>(); 
       
  6825 }
       
  6826     
       
  6827 /*! \internal */
       
  6828 bool QMailMessage::contentModified() const
       
  6829 {
       
  6830     return partContainerImpl()->contentModified();
       
  6831 }
       
  6832 
       
  6833 /*! \internal */
       
  6834 void QMailMessage::setUnmodified()
       
  6835 {
       
  6836     metaDataImpl()->setUnmodified();
       
  6837     partContainerImpl()->setUnmodified();
       
  6838 }
       
  6839 
       
  6840 /*! \internal */
       
  6841 void QMailMessage::setHeader(const QMailMessageHeader& partHeader, const QMailMessagePartContainerPrivate* parent)
       
  6842 {
       
  6843     QMailMessagePartContainer::setHeader(partHeader, parent);
       
  6844 
       
  6845     // See if any of the header fields need to be propagated to the meta data object
       
  6846     foreach (const QMailMessageHeaderField& field, headerFields()) {
       
  6847         QByteArray duplicatedId(duplicatedData(field.id()));
       
  6848         if (!duplicatedId.isNull()) {
       
  6849             updateMetaData(duplicatedId, field.decodedContent());
       
  6850         }
       
  6851     }
       
  6852 }
       
  6853 
       
  6854 /*! \internal */
       
  6855 QByteArray QMailMessage::duplicatedData(const QString& id) const
       
  6856 {
       
  6857     // These items are duplicated in both the message content and the meta data
       
  6858     QByteArray plainId( to7BitAscii(id).trimmed().toLower() );
       
  6859 
       
  6860     if ((plainId == "from") || (plainId == "to") || (plainId == "subject") || (plainId == "date"))
       
  6861         return plainId;
       
  6862 
       
  6863     return QByteArray();
       
  6864 }
       
  6865 
       
  6866 /*! \internal */
       
  6867 void QMailMessage::updateMetaData(const QByteArray& id, const QString& value)
       
  6868 {
       
  6869     if (id == "from") {
       
  6870         metaDataImpl()->setFrom(value);
       
  6871     } else if (id == "to") {
       
  6872         metaDataImpl()->setTo(value);
       
  6873     } else if (id == "subject") {
       
  6874         metaDataImpl()->setSubject(value);
       
  6875     } else if (id == "date") {
       
  6876         metaDataImpl()->setDate(QMailTimeStamp(value));
       
  6877     }
       
  6878 }
       
  6879 
       
  6880 /*! \internal */
       
  6881 QMailMessage QMailMessage::fromRfc2822(LongString& ls)
       
  6882 {
       
  6883     const QByteArray terminator((QByteArray(QMailMessage::CRLF) + QMailMessage::CRLF));
       
  6884 
       
  6885     QMailMessage mail;
       
  6886 
       
  6887     int pos = ls.indexOf(terminator);
       
  6888     if (pos == -1) {
       
  6889         // No body? Parse entirety as header
       
  6890         mail.setHeader( QMailMessageHeader( ls.toQByteArray() ) );
       
  6891     } else {
       
  6892         // Parse the header part to know what we've got
       
  6893         mail.setHeader( QMailMessageHeader( ls.left(pos).toQByteArray() ) );
       
  6894 
       
  6895         // Parse the remainder as content
       
  6896         mail.partContainerImpl()->fromRfc2822( ls.mid(pos + 4) );
       
  6897     }
       
  6898 
       
  6899     return mail;
       
  6900 }
       
  6901 
       
  6902 /*! 
       
  6903     \fn QMailMessage::serialize(Stream&) const
       
  6904     \internal 
       
  6905 */
       
  6906 template <typename Stream> 
       
  6907 void QMailMessage::serialize(Stream &stream) const
       
  6908 {
       
  6909     metaDataImpl()->serialize(stream);
       
  6910     partContainerImpl()->serialize(stream);
       
  6911 }
       
  6912 
       
  6913 template void QMailMessage::serialize(QDataStream &) const;
       
  6914 
       
  6915 /*! 
       
  6916     \fn QMailMessage::deserialize(Stream&)
       
  6917     \internal 
       
  6918 */
       
  6919 template <typename Stream> 
       
  6920 void QMailMessage::deserialize(Stream &stream)
       
  6921 {
       
  6922     metaDataImpl()->deserialize(stream);
       
  6923     partContainerImpl()->deserialize(stream);
       
  6924 }
       
  6925 
       
  6926 template void QMailMessage::deserialize(QDataStream &);
       
  6927