aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Dmitry Isaikin <isaikin-dmitry@yandex.ru>2016-06-06 04:28:53 +0400
committerGravatar Hoà V. DINH <dinh.viet.hoa@gmail.com>2016-06-05 17:28:53 -0700
commiteeed76e48c830fe742eadd435682f3b1e6036f83 (patch)
tree36bad63fccde47e6cdecb7033f65a3d75e1bbb71 /src/core
parent6dbe79ccf3f085da9a195ee3f9a59cb4449f6505 (diff)
Add possibility of fetching message attachment by chunks and store it to file. (#1438)
Diffstat (limited to 'src/core')
-rw-r--r--src/core/basetypes/MCData.cpp170
-rw-r--r--src/core/basetypes/MCData.h4
-rw-r--r--src/core/basetypes/MCDataDecoderUtils.cpp192
-rw-r--r--src/core/basetypes/MCDataDecoderUtils.h25
-rw-r--r--src/core/basetypes/MCDataStreamDecoder.cpp105
-rw-r--r--src/core/basetypes/MCDataStreamDecoder.h51
-rw-r--r--src/core/imap/MCIMAPSession.cpp162
-rw-r--r--src/core/imap/MCIMAPSession.h13
-rw-r--r--src/core/rfc822/MCAttachment.cpp4
9 files changed, 551 insertions, 175 deletions
diff --git a/src/core/basetypes/MCData.cpp b/src/core/basetypes/MCData.cpp
index 796f580e..1fee18aa 100644
--- a/src/core/basetypes/MCData.cpp
+++ b/src/core/basetypes/MCData.cpp
@@ -28,6 +28,7 @@
#include "MCBase64.h"
#include "MCSet.h"
#include "MCLock.h"
+#include "MCDataDecoderUtils.h"
#define MCDATA_DEFAULT_CHARSET "iso-8859-1"
@@ -565,170 +566,10 @@ Data * Data::dataWithContentsOfFile(String * filename)
return data;
}
-static size_t uudecode(const char * text, size_t size, char * dst, size_t dst_buf_size)
-{
- unsigned int count = 0;
- const char *b = text; /* beg */
- const char *s = b; /* src */
- const char *e = b+size; /* end */
- char *d = dst;
- int out = (*s++ & 0x7f) - 0x20;
-
- /* don't process lines without leading count character */
- if (out < 0)
- return size;
-
- /* dummy check. user must allocate buffer with appropriate length */
- if (dst_buf_size < out)
- return size;
-
- /* don't process begin and end lines */
- if ((strncasecmp((const char *)b, "begin ", 6) == 0) ||
- (strncasecmp((const char *)b, "end", 3) == 0))
- return size;
-
- //while (s < e - 4)
- while (s < e && count < out)
- {
- int v = 0;
- int i;
- for (i = 0; i < 4; i += 1) {
- char c = *s++;
- v = v << 6 | ((c - 0x20) & 0x3F);
- }
- for (i = 2; i >= 0; i -= 1) {
- char c = (char) (v & 0xFF);
- d[i] = c;
- v = v >> 8;
- }
- d += 3;
- count += 3;
- }
- return count;
-}
-
-static void decodedPartDeallocator(char * decoded, unsigned int decoded_length) {
- mailmime_decoded_part_free(decoded);
-};
-
Data * Data::decodedDataUsingEncoding(Encoding encoding)
{
- const char * text;
- size_t text_length;
-
- text = bytes();
- text_length = length();
-
- switch (encoding) {
- case Encoding7Bit:
- case Encoding8Bit:
- case EncodingBinary:
- case EncodingOther:
- default:
- {
- return this;
- }
- case EncodingBase64:
- case EncodingQuotedPrintable:
- {
- char * decoded;
- size_t decoded_length;
- size_t cur_token;
- int mime_encoding;
- Data * data;
-
- switch (encoding) {
- default: //disable warning
- case EncodingBase64:
- mime_encoding = MAILMIME_MECHANISM_BASE64;
- break;
- case EncodingQuotedPrintable:
- mime_encoding = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
- break;
- }
-
- cur_token = 0;
- mailmime_part_parse(text, text_length, &cur_token,
- mime_encoding, &decoded, &decoded_length);
-
- data = Data::data();
- data->takeBytesOwnership(decoded, (unsigned int) decoded_length, decodedPartDeallocator);
- return data;
- }
- case EncodingUUEncode:
- {
- Data * data;
- const char * current_p;
-
- data = Data::dataWithCapacity((unsigned int) text_length);
-
- current_p = text;
- while (1) {
- /* In uuencoded files each data line usually have 45 bytes of decoded data.
- Maximum possible length is limited by (0x7f-0x20) bytes.
- So 256-bytes buffer is enough. */
- char decoded_buf[256];
- size_t decoded_length;
- size_t length;
- const char * p;
- const char * p1;
- const char * p2;
- const char * end_line;
-
- p1 = strchr(current_p, '\n');
- p2 = strchr(current_p, '\r');
- if (p1 == NULL) {
- p = p2;
- }
- else if (p2 == NULL) {
- p = p1;
- }
- else {
- if (p1 - current_p < p2 - current_p) {
- p = p1;
- }
- else {
- p = p2;
- }
- }
- end_line = p;
- if (p != NULL) {
- while ((size_t) (p - text) < text_length) {
- if ((* p != '\r') && (* p != '\n')) {
- break;
- }
- p ++;
- }
- }
- if (p == NULL) {
- length = text_length - (current_p - text);
- }
- else {
- length = end_line - current_p;
- }
- if (length == 0) {
- break;
- }
- decoded_length = uudecode(current_p, length, decoded_buf, sizeof(decoded_buf));
- if (decoded_length != 0 && decoded_length < length) {
- data->appendBytes(decoded_buf, (unsigned int) decoded_length);
- }
-
- if (p == NULL)
- break;
-
- current_p = p;
- while ((size_t) (current_p - text) < text_length) {
- if ((* current_p != '\r') && (* current_p != '\n')) {
- break;
- }
- current_p ++;
- }
- }
-
- return data;
- }
- }
+ Data * unused = NULL;
+ return MCDecodeData(this, encoding, false, &unused);
}
Data * Data::data()
@@ -765,11 +606,14 @@ void Data::importSerializable(HashMap * serializable)
ErrorCode Data::writeToFile(String * filename)
{
FILE * f = fopen(filename->fileSystemRepresentation(), "wb");
+
if (f == NULL) {
return ErrorFile;
}
size_t result = fwrite(bytes(), length(), 1, f);
- fclose(f);
+ if (fclose(f) != 0) {
+ return ErrorFile;
+ }
if (result == 0) {
return ErrorFile;
}
diff --git a/src/core/basetypes/MCData.h b/src/core/basetypes/MCData.h
index 11c0d6c2..ef7acda3 100644
--- a/src/core/basetypes/MCData.h
+++ b/src/core/basetypes/MCData.h
@@ -46,11 +46,11 @@ namespace mailcore {
virtual String * stringWithDetectedCharset(String * charset, bool isHTML);
virtual String * stringWithCharset(const char * charset);
virtual Data * decodedDataUsingEncoding(Encoding encoding);
-
+
virtual String * base64String();
virtual ErrorCode writeToFile(String * filename);
-
+
public: // private
virtual String * charsetWithFilteredHTML(bool filterHTML, String * hintCharset = NULL);
diff --git a/src/core/basetypes/MCDataDecoderUtils.cpp b/src/core/basetypes/MCDataDecoderUtils.cpp
new file mode 100644
index 00000000..4ea739ce
--- /dev/null
+++ b/src/core/basetypes/MCDataDecoderUtils.cpp
@@ -0,0 +1,192 @@
+#include "MCDataDecoderUtils.h"
+
+#include <libetpan/libetpan.h>
+
+#include <string.h>
+#include <strings.h>
+
+namespace mailcore {
+
+static size_t uudecode(const char * text, size_t size, char * dst, size_t dst_buf_size)
+{
+ unsigned int count = 0;
+ const char *b = text; /* beg */
+ const char *s = b; /* src */
+ const char *e = b+size; /* end */
+ char *d = dst;
+ int out = (*s++ & 0x7f) - 0x20;
+
+ /* don't process lines without leading count character */
+ if (out < 0)
+ return size;
+
+ /* dummy check. user must allocate buffer with appropriate length */
+ if (dst_buf_size < out)
+ return size;
+
+ /* don't process begin and end lines */
+ if ((strncasecmp((const char *)b, "begin ", 6) == 0) ||
+ (strncasecmp((const char *)b, "end", 3) == 0))
+ return size;
+
+ while (s < e && count < out)
+ {
+ int v = 0;
+ int i;
+ for (i = 0; i < 4; i += 1) {
+ char c = *s++;
+ v = v << 6 | ((c - 0x20) & 0x3F);
+ }
+ for (i = 2; i >= 0; i -= 1) {
+ char c = (char) (v & 0xFF);
+ d[i] = c;
+ v = v >> 8;
+ }
+ d += 3;
+ count += 3;
+ }
+ return count;
+}
+
+static void decodedPartDeallocator(char * decoded, unsigned int decoded_length) {
+ mailmime_decoded_part_free(decoded);
+}
+
+Data * MCDecodeData(Data * encodedData, Encoding encoding, bool partialContent, Data ** pRemainingData)
+{
+ const char * text;
+ size_t text_length;
+
+ text = encodedData->bytes();
+ text_length = encodedData->length();
+
+ * pRemainingData = NULL;
+
+ switch (encoding) {
+ case Encoding7Bit:
+ case Encoding8Bit:
+ case EncodingBinary:
+ case EncodingOther:
+ default:
+ {
+ return encodedData;
+ }
+ case EncodingBase64:
+ case EncodingQuotedPrintable:
+ {
+ char * decoded;
+ size_t decoded_length;
+ size_t cur_token;
+ int mime_encoding;
+
+ switch (encoding) {
+ default: //disable warning
+ case EncodingBase64:
+ mime_encoding = MAILMIME_MECHANISM_BASE64;
+ break;
+ case EncodingQuotedPrintable:
+ mime_encoding = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
+ break;
+ }
+
+ cur_token = 0;
+ if (partialContent) {
+ mailmime_part_parse_partial(text, text_length, &cur_token,
+ mime_encoding, &decoded, &decoded_length);
+ }
+ else {
+ mailmime_part_parse(text, text_length, &cur_token,
+ mime_encoding, &decoded, &decoded_length);
+ }
+
+ if (cur_token < text_length) {
+ * pRemainingData = Data::dataWithBytes(text + cur_token, (unsigned int)(text_length - cur_token));
+ }
+
+ Data * data = Data::data();
+ data->takeBytesOwnership(decoded, (unsigned int) decoded_length, decodedPartDeallocator);
+ return data;
+ }
+ case EncodingUUEncode:
+ {
+ Data * data;
+ const char * current_p;
+
+ data = Data::dataWithCapacity((unsigned int) text_length);
+
+ current_p = text;
+ while (1) {
+ /* In uuencoded files each data line usually have 45 bytes of decoded data.
+ Maximum possible length is limited by (0x7f-0x20) bytes.
+ So 256-bytes buffer is enough. */
+ char decoded_buf[256];
+ size_t decoded_length;
+ size_t length;
+ const char * p;
+ const char * p1;
+ const char * p2;
+ const char * end_line;
+
+ p1 = strchr(current_p, '\n');
+ p2 = strchr(current_p, '\r');
+ if (p1 == NULL) {
+ p = p2;
+ }
+ else if (p2 == NULL) {
+ p = p1;
+ }
+ else {
+ if (p1 - current_p < p2 - current_p) {
+ p = p1;
+ }
+ else {
+ p = p2;
+ }
+ }
+ end_line = p;
+ if (partialContent && (p1 == NULL || p2 == NULL) &&
+ ((end_line == NULL) || (end_line - text) == (text_length - 1))) {
+ // possibly partial content detected
+ * pRemainingData = Data::dataWithBytes(current_p, (unsigned int)(text_length - (current_p - text)));
+ break;
+ }
+ if (p != NULL) {
+ while ((size_t) (p - text) < text_length) {
+ if ((* p != '\r') && (* p != '\n')) {
+ break;
+ }
+ p ++;
+ }
+ }
+ if (p == NULL) {
+ length = text_length - (current_p - text);
+ }
+ else {
+ length = end_line - current_p;
+ }
+ if (length == 0) {
+ break;
+ }
+ decoded_length = uudecode(current_p, length, decoded_buf, sizeof(decoded_buf));
+ if (decoded_length != 0 && decoded_length < length) {
+ data->appendBytes(decoded_buf, (unsigned int) decoded_length);
+ }
+
+ if (p == NULL)
+ break;
+
+ current_p = p;
+ while ((size_t) (current_p - text) < text_length) {
+ if ((* current_p != '\r') && (* current_p != '\n')) {
+ break;
+ }
+ current_p ++;
+ }
+ }
+
+ return data;
+ }
+ }
+}
+
+}
diff --git a/src/core/basetypes/MCDataDecoderUtils.h b/src/core/basetypes/MCDataDecoderUtils.h
new file mode 100644
index 00000000..1481a2b4
--- /dev/null
+++ b/src/core/basetypes/MCDataDecoderUtils.h
@@ -0,0 +1,25 @@
+//
+// MCDataDecoderUtils.h
+// mailcore2
+//
+// Copyright © 2016 MailCore. All rights reserved.
+//
+
+#ifndef MAILCORE_MCDATADECODERUTILS_H
+
+#define MAILCORE_MCDATADECODERUTILS_H
+
+#include <MailCore/MCData.h>
+#include <MailCore/MCMessageConstants.h>
+
+#ifdef __cplusplus
+
+namespace mailcore {
+
+ Data * MCDecodeData(Data * encodedData, Encoding encoding, bool partialContent, Data ** pRemainingData);
+
+}
+
+#endif
+
+#endif
diff --git a/src/core/basetypes/MCDataStreamDecoder.cpp b/src/core/basetypes/MCDataStreamDecoder.cpp
new file mode 100644
index 00000000..120cfb65
--- /dev/null
+++ b/src/core/basetypes/MCDataStreamDecoder.cpp
@@ -0,0 +1,105 @@
+#include "MCDataStreamDecoder.h"
+
+#include "MCString.h"
+#include "MCUtils.h"
+#include "MCDataDecoderUtils.h"
+
+using namespace mailcore;
+
+DataStreamDecoder::DataStreamDecoder()
+{
+ mFilename = NULL;
+ mEncoding = Encoding7Bit;
+ mRemainingData = NULL;
+ mFile = NULL;
+}
+
+DataStreamDecoder::~DataStreamDecoder()
+{
+ MC_SAFE_RELEASE(mRemainingData);
+ MC_SAFE_RELEASE(mFilename);
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+void DataStreamDecoder::setEncoding(Encoding encoding)
+{
+ mEncoding = encoding;
+}
+
+void DataStreamDecoder::setFilename(String * filename)
+{
+ MC_SAFE_REPLACE_COPY(String, mFilename, filename);
+}
+
+ErrorCode DataStreamDecoder::appendData(Data * data)
+{
+ Data * dataForDecode;
+ if (mRemainingData && mRemainingData->length()) {
+ // the data remains from previous append
+ dataForDecode = (Data *) MC_SAFE_COPY(mRemainingData);
+ dataForDecode->appendData(data);
+ } else {
+ dataForDecode = (Data *) MC_SAFE_RETAIN(data);
+ }
+
+ Data * remainingData = NULL;
+ Data * decodedData = MCDecodeData(dataForDecode, mEncoding, true, &remainingData);
+
+ ErrorCode errorCode = appendDecodedData(decodedData);
+
+ if (errorCode == ErrorNone) {
+ MC_SAFE_REPLACE_RETAIN(Data, mRemainingData, remainingData);
+ }
+
+ MC_SAFE_RELEASE(dataForDecode);
+ return errorCode;
+}
+
+ErrorCode DataStreamDecoder::flushData()
+{
+ if (mRemainingData == NULL || mRemainingData->length() == 0) {
+ return ErrorNone;
+ }
+
+ Data * unused = NULL;
+ Data * decodedData = MCDecodeData(mRemainingData, mEncoding, false, &unused);
+
+ ErrorCode errorCode = appendDecodedData(decodedData);
+
+ if (errorCode == ErrorNone) {
+ if (mFile != NULL) {
+ if (fclose(mFile) != 0) {
+ return ErrorFile;
+ }
+ }
+
+ MC_SAFE_RELEASE(mRemainingData);
+ }
+
+ return errorCode;
+}
+
+ErrorCode DataStreamDecoder::appendDecodedData(Data * decodedData)
+{
+ if (mFilename == NULL) {
+ return ErrorFile;
+ }
+
+ if (mFile == NULL) {
+ mFile = fopen(mFilename->fileSystemRepresentation(), "wb");
+
+ if (mFile == NULL) {
+ return ErrorFile;
+ }
+ }
+
+ size_t result = fwrite(decodedData->bytes(), decodedData->length(), 1, mFile);
+ if (result == 0) {
+ return ErrorFile;
+ }
+
+ return ErrorNone;
+}
diff --git a/src/core/basetypes/MCDataStreamDecoder.h b/src/core/basetypes/MCDataStreamDecoder.h
new file mode 100644
index 00000000..9d04f361
--- /dev/null
+++ b/src/core/basetypes/MCDataStreamDecoder.h
@@ -0,0 +1,51 @@
+//
+// DataStreamDecoder.hpp
+// mailcore2
+//
+// Copyright © 2016 MailCore. All rights reserved.
+//
+
+#ifndef MAILCORE_MCDATASTREAMDECODER_H
+
+#define MAILCORE_MCDATASTREAMDECODER_H
+
+#include <stdlib.h>
+
+#include <MailCore/MCObject.h>
+#include <MailCore/MCData.h>
+#include <MailCore/MCMessageConstants.h>
+
+#ifdef __cplusplus
+
+namespace mailcore {
+
+ class DataStreamDecoder : public Object {
+ public:
+ DataStreamDecoder();
+ virtual ~DataStreamDecoder();
+
+ virtual void setEncoding(Encoding encoding);
+ // output filename
+ virtual void setFilename(String * filename);
+
+ // when data are received, decode them and add them to the file.
+ virtual ErrorCode appendData(Data * data);
+
+ // end of data received.
+ virtual ErrorCode flushData();
+
+ private: // impl
+ virtual ErrorCode appendDecodedData(Data * decodedData);
+
+ private:
+ String * mFilename;
+ Encoding mEncoding;
+ Data * mRemainingData;
+ FILE * mFile;
+ };
+
+}
+
+#endif
+
+#endif
diff --git a/src/core/imap/MCIMAPSession.cpp b/src/core/imap/MCIMAPSession.cpp
index 61be95c3..1357f47f 100644
--- a/src/core/imap/MCIMAPSession.cpp
+++ b/src/core/imap/MCIMAPSession.cpp
@@ -27,9 +27,63 @@
#include "MCCertificateUtils.h"
#include "MCIMAPIdentity.h"
#include "MCLibetpan.h"
+#include "MCDataStreamDecoder.h"
using namespace mailcore;
+class LoadByChunkProgress : public Object, public IMAPProgressCallback {
+public:
+ LoadByChunkProgress();
+ virtual ~LoadByChunkProgress();
+
+ virtual void setOffset(uint32_t offset);
+ virtual void setEstimatedSize(uint32_t estimatedSize);
+ virtual void setProgressCallback(IMAPProgressCallback * progressCallback);
+
+ virtual void bodyProgress(IMAPSession * session, unsigned int current, unsigned int maximum);
+
+private:
+ uint32_t mOffset;
+ uint32_t mEstimatedSize;
+ IMAPProgressCallback * mProgressCallback; // non retained
+};
+
+LoadByChunkProgress::LoadByChunkProgress()
+{
+ mOffset = 0;
+ mEstimatedSize = 0;
+ mProgressCallback = NULL;
+}
+
+LoadByChunkProgress::~LoadByChunkProgress()
+{
+}
+
+void LoadByChunkProgress::setOffset(uint32_t offset)
+{
+ mOffset = offset;
+}
+
+void LoadByChunkProgress::setEstimatedSize(uint32_t estimatedSize)
+{
+ mEstimatedSize = estimatedSize;
+}
+
+void LoadByChunkProgress::setProgressCallback(IMAPProgressCallback * progressCallback)
+{
+ mProgressCallback = progressCallback;
+}
+
+void LoadByChunkProgress::bodyProgress(IMAPSession * session, unsigned int current, unsigned int maximum)
+{
+ // In case of loading attachment by chunks we need report overall progress
+ if (mEstimatedSize > 0 && mEstimatedSize > maximum) {
+ maximum = mEstimatedSize;
+ current += mOffset;
+ }
+ mProgressCallback->bodyProgress(session, current, maximum);
+}
+
enum {
STATE_DISCONNECTED,
STATE_CONNECTED,
@@ -2813,8 +2867,9 @@ static void nstringDeallocator(char * bytes, unsigned int length) {
mailimap_nstring_free(bytes);
};
-Data * IMAPSession::fetchMessageAttachment(String * folder, bool identifier_is_uid,
+Data * IMAPSession::fetchNonDecodedMessageAttachment(String * folder, bool identifier_is_uid,
uint32_t identifier, String * partID,
+ bool wholePart, uint32_t offset, uint32_t length,
Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError)
{
struct mailimap_fetch_type * fetch_type;
@@ -2827,21 +2882,21 @@ Data * IMAPSession::fetchMessageAttachment(String * folder, bool identifier_is_u
char * text = NULL;
size_t text_length = 0;
Data * data;
-
+
selectIfNeeded(folder, pError);
if (* pError != ErrorNone)
return NULL;
-
+
mProgressItemsCount = 0;
mProgressCallback = progressCallback;
bodyProgress(0, 0);
-
+
partIDArray = partID->componentsSeparatedByString(MCSTR("."));
sec_list = clist_new();
for(unsigned int i = 0 ; i < partIDArray->count() ; i ++) {
uint32_t * value;
String * element;
-
+
element = (String *) partIDArray->objectAtIndex(i);
value = (uint32_t *) malloc(sizeof(* value));
* value = element->intValue();
@@ -2849,7 +2904,12 @@ Data * IMAPSession::fetchMessageAttachment(String * folder, bool identifier_is_u
}
section_part = mailimap_section_part_new(sec_list);
section = mailimap_section_new_part(section_part);
- fetch_att = mailimap_fetch_att_new_body_peek_section(section);
+ if (wholePart) {
+ fetch_att = mailimap_fetch_att_new_body_peek_section(section);
+ }
+ else {
+ fetch_att = mailimap_fetch_att_new_body_peek_section_partial(section, offset, length);
+ }
fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att);
#ifdef LIBETPAN_HAS_MAILIMAP_RAMBLER_WORKAROUND
@@ -2885,13 +2945,23 @@ Data * IMAPSession::fetchMessageAttachment(String * folder, bool identifier_is_u
data = Data::data();
data->takeBytesOwnership(text, (unsigned int) text_length, nstringDeallocator);
- data = data->decodedDataUsingEncoding(encoding);
* pError = ErrorNone;
return data;
}
+Data * IMAPSession::fetchMessageAttachment(String * folder, bool identifier_is_uid,
+ uint32_t identifier, String * partID,
+ Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError)
+{
+ Data * data = fetchNonDecodedMessageAttachment(folder, identifier_is_uid, identifier, partID, true, 0, 0, encoding, progressCallback, pError);
+ if (data) {
+ data = data->decodedDataUsingEncoding(encoding);
+ }
+ return data;
+}
+
Data * IMAPSession::fetchMessageAttachmentByUID(String * folder, uint32_t uid, String * partID,
Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError)
{
@@ -2904,6 +2974,84 @@ Data * IMAPSession::fetchMessageAttachmentByNumber(String * folder, uint32_t num
return fetchMessageAttachment(folder, false, number, partID, encoding, progressCallback, pError);
}
+void IMAPSession::fetchMessageAttachmentToFileByUID(String * folder, uint32_t uid, String * partID,
+ uint32_t estimatedSize, Encoding encoding,
+ String * outputFile, uint32_t chunkSize,
+ IMAPProgressCallback * progressCallback, ErrorCode * pError)
+{
+ DataStreamDecoder * decoder = new DataStreamDecoder();
+ decoder->setEncoding(encoding);
+ decoder->setFilename(outputFile);
+
+ int nRetries = 0;
+ int const maxRetries = 3;
+ ErrorCode error = ErrorNone;
+ uint32_t offset = 0;
+ while (1) {
+ AutoreleasePool * pool = new AutoreleasePool();
+
+ LoadByChunkProgress * chunkProgressCallback = new LoadByChunkProgress();
+ chunkProgressCallback->setOffset(offset);
+ chunkProgressCallback->setEstimatedSize(estimatedSize);
+ chunkProgressCallback->setProgressCallback(progressCallback);
+
+ Data * data = fetchNonDecodedMessageAttachment(folder, true, uid, partID, false, offset, chunkSize, encoding, chunkProgressCallback, &error);
+
+ MC_SAFE_RELEASE(chunkProgressCallback);
+
+ if (error != ErrorNone) {
+ pool->release();
+ if ((error == ErrorConnection || error == ErrorParse) && nRetries < maxRetries) {
+ error = ErrorNone;
+ nRetries++;
+ continue;
+ }
+ break;
+ } else {
+ nRetries = 0;
+ }
+
+ if (data == NULL) {
+ break;
+ }
+
+ uint32_t encodedSize = data->length();
+ if (encodedSize == 0) {
+ pool->release();
+ break;
+ }
+
+ error = decoder->appendData(data);
+
+ pool->release();
+
+ if (error != ErrorNone) {
+ break;
+ }
+
+ offset += encodedSize;
+
+ // Try detect is this chunk last.
+ // Estimated size (extracted from BODYSTRUCTURE info) may be incorrect.
+ // Also, server may return chunk with size less than requested.
+ // So this detection is some tricky.
+ bool endOfPart = ((encodedSize == 0) ||
+ (estimatedSize > 0 && (estimatedSize <= offset) && (encodedSize != chunkSize)) ||
+ (estimatedSize == 0 && encodedSize < chunkSize));
+ if (endOfPart) {
+ break;
+ }
+ }
+
+ if (error == ErrorNone) {
+ decoder->flushData();
+ }
+
+ MC_SAFE_RELEASE(decoder);
+
+ * pError = error;
+}
+
IndexSet * IMAPSession::search(String * folder, IMAPSearchKind kind, String * searchString, ErrorCode * pError)
{
IMAPSearchExpression * expr;
diff --git a/src/core/imap/MCIMAPSession.h b/src/core/imap/MCIMAPSession.h
index adbecd43..55f47de0 100644
--- a/src/core/imap/MCIMAPSession.h
+++ b/src/core/imap/MCIMAPSession.h
@@ -121,6 +121,12 @@ namespace mailcore {
IMAPProgressCallback * progressCallback, ErrorCode * pError);
virtual Data * fetchMessageAttachmentByUID(String * folder, uint32_t uid, String * partID,
Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError);
+
+ virtual void fetchMessageAttachmentToFileByUID(String * folder, uint32_t uid, String * partID,
+ uint32_t estimatedSize, Encoding encoding,
+ String * outputFile, uint32_t chunkSize,
+ IMAPProgressCallback * progressCallback, ErrorCode * pError);
+
virtual Data * fetchMessageAttachmentByNumber(String * folder, uint32_t number, String * partID,
Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError);
virtual HashMap * fetchMessageNumberUIDMapping(String * folder, uint32_t fromUID, uint32_t toUID,
@@ -305,9 +311,14 @@ namespace mailcore {
Data * fetchMessageAttachment(String * folder, bool identifier_is_uid,
uint32_t identifier, String * partID,
Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError);
+ // in case of wholePart is false, receives range [offset, length]
+ Data * fetchNonDecodedMessageAttachment(String * folder, bool identifier_is_uid,
+ uint32_t identifier, String * partID,
+ bool wholePart, uint32_t offset, uint32_t length,
+ Encoding encoding, IMAPProgressCallback * progressCallback, ErrorCode * pError);
void storeLabels(String * folder, bool identifier_is_uid, IndexSet * identifiers, IMAPStoreFlagsRequestKind kind, Array * labels, ErrorCode * pError);
};
-
+
}
#endif
diff --git a/src/core/rfc822/MCAttachment.cpp b/src/core/rfc822/MCAttachment.cpp
index e6ebc9c9..0881c9a8 100644
--- a/src/core/rfc822/MCAttachment.cpp
+++ b/src/core/rfc822/MCAttachment.cpp
@@ -548,12 +548,12 @@ Attachment * Attachment::attachmentWithSingleMIME(struct mailmime * mime)
mailmime_single_fields_init(&single_fields, mime->mm_mime_fields, mime->mm_content_type);
encoding = encodingForMIMEEncoding(single_fields.fld_encoding, data->dt_encoding);
-
+
Data * mimeData;
mimeData = Data::dataWithBytes(bytes, (unsigned int) length);
mimeData = mimeData->decodedDataUsingEncoding(encoding);
result->setData(mimeData);
-
+
str = get_content_type_str(mime->mm_content_type);
result->setMimeType(String::stringWithUTF8Characters(str));
free(str);