|
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 |