From eeed76e48c830fe742eadd435682f3b1e6036f83 Mon Sep 17 00:00:00 2001 From: Dmitry Isaikin Date: Mon, 6 Jun 2016 04:28:53 +0400 Subject: Add possibility of fetching message attachment by chunks and store it to file. (#1438) --- build-mac/mailcore2.xcodeproj/project.pbxproj | 44 ++++- build-windows/build_headers.list | 4 + .../mailcore2/mailcore2/mailcore2.vcxproj | 6 + .../mailcore2/mailcore2/mailcore2.vcxproj.filters | 18 ++ src/async/imap/MCAsyncIMAP.h | 1 + src/async/imap/MCIMAPAsyncConnection.cpp | 1 + src/async/imap/MCIMAPAsyncConnection.h | 1 + src/async/imap/MCIMAPAsyncSession.cpp | 19 ++ src/async/imap/MCIMAPAsyncSession.h | 9 +- .../imap/MCIMAPFetchContentToFileOperation.cpp | 91 ++++++++++ src/async/imap/MCIMAPFetchContentToFileOperation.h | 53 ++++++ src/cmake/async.cmake | 1 + src/cmake/core.cmake | 2 + src/cmake/objc.cmake | 1 + src/cmake/public-headers.cmake | 4 + src/core/basetypes/MCData.cpp | 170 +----------------- src/core/basetypes/MCData.h | 4 +- src/core/basetypes/MCDataDecoderUtils.cpp | 192 +++++++++++++++++++++ src/core/basetypes/MCDataDecoderUtils.h | 25 +++ src/core/basetypes/MCDataStreamDecoder.cpp | 105 +++++++++++ src/core/basetypes/MCDataStreamDecoder.h | 51 ++++++ src/core/imap/MCIMAPSession.cpp | 162 ++++++++++++++++- src/core/imap/MCIMAPSession.h | 13 +- src/core/rfc822/MCAttachment.cpp | 4 +- src/objc/imap/MCOIMAP.h | 1 + src/objc/imap/MCOIMAPFetchContentToFileOperation.h | 51 ++++++ .../imap/MCOIMAPFetchContentToFileOperation.mm | 103 +++++++++++ src/objc/imap/MCOIMAPSession.h | 32 ++++ src/objc/imap/MCOIMAPSession.mm | 16 ++ 29 files changed, 1006 insertions(+), 178 deletions(-) create mode 100644 src/async/imap/MCIMAPFetchContentToFileOperation.cpp create mode 100644 src/async/imap/MCIMAPFetchContentToFileOperation.h create mode 100644 src/core/basetypes/MCDataDecoderUtils.cpp create mode 100644 src/core/basetypes/MCDataDecoderUtils.h create mode 100644 src/core/basetypes/MCDataStreamDecoder.cpp create mode 100644 src/core/basetypes/MCDataStreamDecoder.h create mode 100644 src/objc/imap/MCOIMAPFetchContentToFileOperation.h create mode 100644 src/objc/imap/MCOIMAPFetchContentToFileOperation.mm diff --git a/build-mac/mailcore2.xcodeproj/project.pbxproj b/build-mac/mailcore2.xcodeproj/project.pbxproj index 3ab90564..5db6ed6b 100755 --- a/build-mac/mailcore2.xcodeproj/project.pbxproj +++ b/build-mac/mailcore2.xcodeproj/project.pbxproj @@ -42,6 +42,18 @@ 4BE4029217B548D900ECC5E4 /* MCIMAPQuotaOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4B3C1BE017ABF4BC008BBF4C /* MCIMAPQuotaOperation.h */; }; 636F55A01BCFA9A600AA00DB /* MailCore.h in Headers */ = {isa = PBXBuildFile; fileRef = C64EA7A4169F2A3E00778456 /* MailCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; 636F55A11BCFA9A600AA00DB /* MailCore.h in Headers */ = {isa = PBXBuildFile; fileRef = C64EA7A4169F2A3E00778456 /* MailCore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 810DB78D1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810DB78B1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.cpp */; }; + 810DB78E1C68F4F000017B12 /* MCIMAPFetchContentToFileOperation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 810DB78B1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.cpp */; }; + 810DB7911C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 810DB7901C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.mm */; }; + 810DB7921C68F50B00017B12 /* MCOIMAPFetchContentToFileOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 810DB7901C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.mm */; }; + 811320AF1D02388A004B7ECF /* MCDataDecoderUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 811320AE1D02388A004B7ECF /* MCDataDecoderUtils.cpp */; }; + 811320B01D02388A004B7ECF /* MCDataDecoderUtils.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 811320AE1D02388A004B7ECF /* MCDataDecoderUtils.cpp */; }; + 81416BDE1CF8BB17000A4299 /* MCDataStreamDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81416BDC1CF8BB17000A4299 /* MCDataStreamDecoder.cpp */; }; + 81416BDF1CF8BB18000A4299 /* MCDataStreamDecoder.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 81416BDC1CF8BB17000A4299 /* MCDataStreamDecoder.cpp */; }; + 817FA5271C69013C006146BD /* MCIMAPFetchContentToFileOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 810DB78C1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.h */; }; + 817FA5281C69016A006146BD /* MCIMAPFetchContentToFileOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 810DB78C1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.h */; }; + 817FA5291C69037B006146BD /* MCOIMAPFetchContentToFileOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 810DB78F1C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.h */; }; + 817FA52A1C69038F006146BD /* MCOIMAPFetchContentToFileOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 810DB78F1C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.h */; }; 8199FBEB19FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8199FBE819FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.mm */; }; 8199FBEC19FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8199FBE819FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.mm */; }; 8199FBF119FAF1270040BBC3 /* MCIMAPFetchParsedContentOperation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8199FBEF19FAF1270040BBC3 /* MCIMAPFetchParsedContentOperation.cpp */; }; @@ -902,8 +914,8 @@ C6EFFBCD1833334900CFF656 /* MCOIMAPMultiDisconnectOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6EFFBCB1833334900CFF656 /* MCOIMAPMultiDisconnectOperation.mm */; }; C6F49A8F1C8E96D00087F4B7 /* MCIMAPCheckAccountOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA7F716A15A7800778456 /* MCIMAPCheckAccountOperation.h */; }; C6F49A901C8E96D70087F4B7 /* MCIMAPCheckAccountOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA7F716A15A7800778456 /* MCIMAPCheckAccountOperation.h */; }; - C6F49A931C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6F49A921C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm */; settings = {ASSET_TAGS = (); }; }; - C6F49A941C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6F49A921C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm */; settings = {ASSET_TAGS = (); }; }; + C6F49A931C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6F49A921C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm */; }; + C6F49A941C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6F49A921C8EA1450087F4B7 /* MCOIMAPCheckAccountOperation.mm */; }; C6F5B9E216FEA1E800D9DABD /* MCOIMAPMessage.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6F5B9E116FEA1E800D9DABD /* MCOIMAPMessage.mm */; }; C6F5B9E516FEA27500D9DABD /* MCOIMAPMessagePart.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6F5B9E416FEA27500D9DABD /* MCOIMAPMessagePart.mm */; }; C6F5B9E816FEA28600D9DABD /* MCOIMAPMultipart.mm in Sources */ = {isa = PBXBuildFile; fileRef = C6F5B9E716FEA28600D9DABD /* MCOIMAPMultipart.mm */; }; @@ -1039,6 +1051,8 @@ files = ( F38221A81C7B63E500E00721 /* MCONNTPPostOperation.h in CopyFiles */, F38221A71C7B638200E00721 /* MCNNTPPostOperation.h in CopyFiles */, + 817FA5291C69037B006146BD /* MCOIMAPFetchContentToFileOperation.h in CopyFiles */, + 817FA5281C69016A006146BD /* MCIMAPFetchContentToFileOperation.h in CopyFiles */, C6BEC1AB1B1256C100546519 /* MCHTMLCleaner.h in CopyFiles */, 27E91D601A80D3F4005A3244 /* MCMXRecordResolverOperation.h in CopyFiles */, 27478E861A76475F004AE621 /* MCOAccountValidator.h in CopyFiles */, @@ -1278,6 +1292,8 @@ files = ( F38221A61C7B62E900E00721 /* MCONNTPPostOperation.h in CopyFiles */, F38221A51C7B629B00E00721 /* MCNNTPPostOperation.h in CopyFiles */, + 817FA52A1C69038F006146BD /* MCOIMAPFetchContentToFileOperation.h in CopyFiles */, + 817FA5271C69013C006146BD /* MCIMAPFetchContentToFileOperation.h in CopyFiles */, C6BEC1AA1B1256BA00546519 /* MCHTMLCleaner.h in CopyFiles */, 276A65D01A7B7E7D008722C2 /* MCMXRecordResolverOperation.h in CopyFiles */, 27478E881A7647AC004AE621 /* MCOAccountValidator.h in CopyFiles */, @@ -1520,6 +1536,14 @@ 4B3C1BDD17ABF307008BBF4C /* MCOIMAPQuotaOperation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MCOIMAPQuotaOperation.mm; sourceTree = ""; }; 4B3C1BDF17ABF4BB008BBF4C /* MCIMAPQuotaOperation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPQuotaOperation.cpp; sourceTree = ""; }; 4B3C1BE017ABF4BC008BBF4C /* MCIMAPQuotaOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPQuotaOperation.h; sourceTree = ""; }; + 810DB78B1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPFetchContentToFileOperation.cpp; sourceTree = ""; }; + 810DB78C1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPFetchContentToFileOperation.h; sourceTree = ""; }; + 810DB78F1C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCOIMAPFetchContentToFileOperation.h; sourceTree = ""; }; + 810DB7901C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MCOIMAPFetchContentToFileOperation.mm; sourceTree = ""; }; + 811320AD1D0235F5004B7ECF /* MCDataDecoderUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MCDataDecoderUtils.h; sourceTree = ""; }; + 811320AE1D02388A004B7ECF /* MCDataDecoderUtils.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCDataDecoderUtils.cpp; sourceTree = ""; }; + 81416BDC1CF8BB17000A4299 /* MCDataStreamDecoder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCDataStreamDecoder.cpp; sourceTree = ""; }; + 81416BDD1CF8BB17000A4299 /* MCDataStreamDecoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCDataStreamDecoder.h; sourceTree = ""; }; 8199FBE719FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCOIMAPFetchParsedContentOperation.h; sourceTree = ""; }; 8199FBE819FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MCOIMAPFetchParsedContentOperation.mm; sourceTree = ""; }; 8199FBEF19FAF1270040BBC3 /* MCIMAPFetchParsedContentOperation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPFetchParsedContentOperation.cpp; sourceTree = ""; }; @@ -2594,6 +2618,8 @@ C64EA81916A29ADB00778456 /* MCIMAPFetchMessagesOperation.h */, C64EA81B16A29DC100778456 /* MCIMAPFetchContentOperation.cpp */, C64EA81C16A29DC400778456 /* MCIMAPFetchContentOperation.h */, + 810DB78B1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.cpp */, + 810DB78C1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.h */, 8199FBEF19FAF1270040BBC3 /* MCIMAPFetchParsedContentOperation.cpp */, 8199FBF019FAF1270040BBC3 /* MCIMAPFetchParsedContentOperation.h */, C64EA81E16A29E3D00778456 /* MCIMAPStoreFlagsOperation.cpp */, @@ -2745,6 +2771,10 @@ C64EA6A9169E847800778456 /* MCData.cpp */, C64EA6AA169E847800778456 /* MCData.h */, C6D4FD4219FB7DAA001F7E01 /* MCDataMac.mm */, + 811320AE1D02388A004B7ECF /* MCDataDecoderUtils.cpp */, + 811320AD1D0235F5004B7ECF /* MCDataDecoderUtils.h */, + 81416BDC1CF8BB17000A4299 /* MCDataStreamDecoder.cpp */, + 81416BDD1CF8BB17000A4299 /* MCDataStreamDecoder.h */, C64EA6AB169E847800778456 /* MCHash.cpp */, C64EA6AC169E847800778456 /* MCHash.h */, C64EA6AD169E847800778456 /* MCHashMap.cpp */, @@ -3126,6 +3156,8 @@ C6F61F8317016A200073032E /* MCOIMAPFetchMessagesOperation.mm */, C6F61F8517016AD60073032E /* MCOIMAPFetchContentOperation.h */, C6F61F8617016AD60073032E /* MCOIMAPFetchContentOperation.mm */, + 810DB78F1C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.h */, + 810DB7901C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.mm */, 8199FBE719FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.h */, 8199FBE819FAEA440040BBC3 /* MCOIMAPFetchParsedContentOperation.mm */, C6F61F8817016AE60073032E /* MCOIMAPSearchOperation.h */, @@ -3542,6 +3574,7 @@ BDCD7CCB1A70771B0001DCC3 /* csrmbcs.cpp in Sources */, C64EA732169E847800778456 /* MCIMAPSearchExpression.cpp in Sources */, BDCD7CE11A70771B0001DCC3 /* uinvchar.c in Sources */, + 810DB78D1C68F4E200017B12 /* MCIMAPFetchContentToFileOperation.cpp in Sources */, C64EA734169E847800778456 /* MCIMAPSession.cpp in Sources */, C68B2AF717797389005E61EF /* MCConnectionLoggerUtils.cpp in Sources */, C64EA737169E847800778456 /* MCPOPMessageInfo.cpp in Sources */, @@ -3602,6 +3635,7 @@ BDCD7CD11A70771B0001DCC3 /* csrutf8.cpp in Sources */, C62C6EEF16A7B67600737497 /* MCPOPAsyncSession.cpp in Sources */, C62C6EF216A7C6DE00737497 /* MCPOPFetchHeaderOperation.cpp in Sources */, + 81416BDE1CF8BB17000A4299 /* MCDataStreamDecoder.cpp in Sources */, C62C6EF516A7C6EA00737497 /* MCPOPFetchMessageOperation.cpp in Sources */, C62C6EF816A7C6F500737497 /* MCPOPDeleteMessagesOperation.cpp in Sources */, C62C6EFB16A7C94000737497 /* MCPOPOperation.cpp in Sources */, @@ -3644,6 +3678,7 @@ BDCD7CE51A70771B0001DCC3 /* ustring.cpp in Sources */, C64BB23916EDAA3F000DB34C /* MCOAbstractMessagePart.mm in Sources */, C64BB23C16EDAAC7000DB34C /* MCOAbstractMultipart.mm in Sources */, + 811320AF1D02388A004B7ECF /* MCDataDecoderUtils.cpp in Sources */, BDCD7CD71A70771B0001DCC3 /* uarrsort.c in Sources */, 8568A41A1C610F6600FF4470 /* MCOIMAPMoveMessagesOperation.mm in Sources */, C6D4FD4319FB7DAA001F7E01 /* MCDataMac.mm in Sources */, @@ -3728,6 +3763,7 @@ 84D7378F199C02A8005124E5 /* MCONNTPDisconnectOperation.mm in Sources */, BD7C0F461B545CE2003337DF /* MCZipMac.mm in Sources */, C6CF62BD175324CE006398B9 /* MCONetService.mm in Sources */, + 810DB7911C68F50600017B12 /* MCOIMAPFetchContentToFileOperation.mm in Sources */, C6EFFBCC1833334900CFF656 /* MCOIMAPMultiDisconnectOperation.mm in Sources */, C6CF62C6175324F0006398B9 /* MCMailProvider.cpp in Sources */, C6CF62C8175324F0006398B9 /* MCMailProvidersManager.cpp in Sources */, @@ -3821,6 +3857,7 @@ BDCD7CCC1A70771B0001DCC3 /* csrmbcs.cpp in Sources */, C6BA2BB41705F4E6003F0E9E /* MCIMAPPart.cpp in Sources */, BDCD7CE21A70771B0001DCC3 /* uinvchar.c in Sources */, + 810DB78E1C68F4F000017B12 /* MCIMAPFetchContentToFileOperation.cpp in Sources */, C6BA2BB51705F4E6003F0E9E /* MCIMAPSearchExpression.cpp in Sources */, C6BA2BB61705F4E6003F0E9E /* MCIMAPSession.cpp in Sources */, C68B2AF817797389005E61EF /* MCConnectionLoggerUtils.cpp in Sources */, @@ -3881,6 +3918,7 @@ C6BA2BD51705F4E6003F0E9E /* MCIMAPFolderInfoOperation.cpp in Sources */, BDCD7CD21A70771B0001DCC3 /* csrutf8.cpp in Sources */, C6BA2BD61705F4E6003F0E9E /* MCIMAPAsyncConnection.cpp in Sources */, + 81416BDF1CF8BB18000A4299 /* MCDataStreamDecoder.cpp in Sources */, C6BA2BD71705F4E6003F0E9E /* MCPOPAsyncSession.cpp in Sources */, C6BA2BD81705F4E6003F0E9E /* MCPOPFetchHeaderOperation.cpp in Sources */, C6BA2BD91705F4E6003F0E9E /* MCPOPFetchMessageOperation.cpp in Sources */, @@ -3923,6 +3961,7 @@ 84D73765199BFFC7005124E5 /* MCONNTPSession.mm in Sources */, BDCD7CE61A70771B0001DCC3 /* ustring.cpp in Sources */, C6BA2BF01705F4E6003F0E9E /* MCOAbstractMessage.mm in Sources */, + 811320B01D02388A004B7ECF /* MCDataDecoderUtils.cpp in Sources */, C6BA2BF11705F4E6003F0E9E /* MCOAbstractMessagePart.mm in Sources */, 8568A41B1C610F6600FF4470 /* MCOIMAPMoveMessagesOperation.mm in Sources */, BDCD7CD81A70771B0001DCC3 /* uarrsort.c in Sources */, @@ -4007,6 +4046,7 @@ C6CF62BC175324CE006398B9 /* MCOMailProvidersManager.mm in Sources */, BD7C0F471B545CE2003337DF /* MCZipMac.mm in Sources */, 84D73790199C02A8005124E5 /* MCONNTPDisconnectOperation.mm in Sources */, + 810DB7921C68F50B00017B12 /* MCOIMAPFetchContentToFileOperation.mm in Sources */, C6CF62BE175324CE006398B9 /* MCONetService.mm in Sources */, C6EFFBCD1833334900CFF656 /* MCOIMAPMultiDisconnectOperation.mm in Sources */, C6CF62C7175324F0006398B9 /* MCMailProvider.cpp in Sources */, diff --git a/build-windows/build_headers.list b/build-windows/build_headers.list index 3d5a92e2..4662e54c 100644 --- a/build-windows/build_headers.list +++ b/build-windows/build_headers.list @@ -11,6 +11,8 @@ src\core\basetypes\MCString.h src\core\basetypes\MCRange.h src\core\basetypes\MCICUTypes.h src\core\basetypes\MCData.h +src\core\basetypes\MCDataDecoderUtils.h +src\core\basetypes\MCDataStreamDecoder.h src\core\abstract\MCMessageConstants.h src\core\basetypes\MCArray.h src\core\basetypes\MCHashMap.h @@ -88,6 +90,7 @@ src\async\imap\MCIMAPCopyMessagesOperation.h src\async\imap\MCIMAPMoveMessagesOperation.h src\async\imap\MCIMAPFetchMessagesOperation.h src\async\imap\MCIMAPFetchContentOperation.h +src\async\imap\MCIMAPFetchContentToFileOperation.h src\async\imap\MCIMAPFetchParsedContentOperation.h src\async\imap\MCIMAPIdleOperation.h src\async\imap\MCIMAPFolderInfo.h @@ -164,6 +167,7 @@ src\objc\imap\MCOIMAPCopyMessagesOperation.h src\objc\imap\MCOIMAPMoveMessagesOperation.h src\objc\imap\MCOIMAPFetchMessagesOperation.h src\objc\imap\MCOIMAPFetchContentOperation.h +src\objc\imap\MCOIMAPFetchContentToFileOperation.h src\objc\imap\MCOIMAPFetchParsedContentOperation.h src\objc\imap\MCOIMAPSearchOperation.h src\objc\imap\MCOIMAPIdleOperation.h diff --git a/build-windows/mailcore2/mailcore2/mailcore2.vcxproj b/build-windows/mailcore2/mailcore2/mailcore2.vcxproj index 7fd762c2..040d7fe4 100644 --- a/build-windows/mailcore2/mailcore2/mailcore2.vcxproj +++ b/build-windows/mailcore2/mailcore2/mailcore2.vcxproj @@ -33,6 +33,7 @@ + @@ -103,6 +104,8 @@ + + @@ -196,6 +199,7 @@ + @@ -255,6 +259,8 @@ + + diff --git a/build-windows/mailcore2/mailcore2/mailcore2.vcxproj.filters b/build-windows/mailcore2/mailcore2/mailcore2.vcxproj.filters index 77e0f971..8354e6bd 100644 --- a/build-windows/mailcore2/mailcore2/mailcore2.vcxproj.filters +++ b/build-windows/mailcore2/mailcore2/mailcore2.vcxproj.filters @@ -120,6 +120,12 @@ Source Files\core\basetypes + + Source Files\core\basetypes + + + Source Files\core\basetypes + Source Files\core\basetypes @@ -393,6 +399,9 @@ Source Files\async\imap + + Source Files\async\imap + Source Files\async\imap @@ -593,6 +602,12 @@ Source Files\core\basetypes + + Source Files\core\basetypes + + + Source Files\core\basetypes + Source Files\core\basetypes @@ -806,6 +821,9 @@ Source Files\async\imap + + Source Files\async\imap + Source Files\async\imap diff --git a/src/async/imap/MCAsyncIMAP.h b/src/async/imap/MCAsyncIMAP.h index e6d6d367..effa0947 100644 --- a/src/async/imap/MCAsyncIMAP.h +++ b/src/async/imap/MCAsyncIMAP.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/src/async/imap/MCIMAPAsyncConnection.cpp b/src/async/imap/MCIMAPAsyncConnection.cpp index ff12c881..4ae633ec 100644 --- a/src/async/imap/MCIMAPAsyncConnection.cpp +++ b/src/async/imap/MCIMAPAsyncConnection.cpp @@ -21,6 +21,7 @@ #include "MCIMAPCopyMessagesOperation.h" #include "MCIMAPFetchMessagesOperation.h" #include "MCIMAPFetchContentOperation.h" +#include "MCIMAPFetchContentToFileOperation.h" #include "MCIMAPFetchParsedContentOperation.h" #include "MCIMAPStoreFlagsOperation.h" #include "MCIMAPStoreLabelsOperation.h" diff --git a/src/async/imap/MCIMAPAsyncConnection.h b/src/async/imap/MCIMAPAsyncConnection.h index 40b11b90..c665b26d 100644 --- a/src/async/imap/MCIMAPAsyncConnection.h +++ b/src/async/imap/MCIMAPAsyncConnection.h @@ -15,6 +15,7 @@ namespace mailcore { class IMAPCopyMessagesOperation; class IMAPFetchMessagesOperation; class IMAPFetchContentOperation; + class IMAPFetchContentToFileOperation; class IMAPFetchParsedContentOperation; class IMAPIdleOperation; class IMAPFolderInfoOperation; diff --git a/src/async/imap/MCIMAPAsyncSession.cpp b/src/async/imap/MCIMAPAsyncSession.cpp index 5c599558..58dc0dbb 100644 --- a/src/async/imap/MCIMAPAsyncSession.cpp +++ b/src/async/imap/MCIMAPAsyncSession.cpp @@ -29,6 +29,7 @@ #include "MCIMAPMoveMessagesOperation.h" #include "MCIMAPFetchMessagesOperation.h" #include "MCIMAPFetchContentOperation.h" +#include "MCIMAPFetchContentToFileOperation.h" #include "MCIMAPFetchParsedContentOperation.h" #include "MCIMAPStoreFlagsOperation.h" #include "MCIMAPStoreLabelsOperation.h" @@ -588,6 +589,24 @@ IMAPFetchContentOperation * IMAPAsyncSession::fetchMessageAttachmentByUIDOperati return op; } +IMAPFetchContentToFileOperation * IMAPAsyncSession::fetchMessageAttachmentToFileByUIDOperation( + String * folder, uint32_t uid, String * partID, + Encoding encoding, + String * filename, + bool urgent) +{ + IMAPFetchContentToFileOperation * op = new IMAPFetchContentToFileOperation(); + op->setMainSession(this); + op->setFolder(folder); + op->setUid(uid); + op->setPartID(partID); + op->setEncoding(encoding); + op->setFilename(filename); + op->setUrgent(urgent); + op->autorelease(); + return op; +} + IMAPFetchContentOperation * IMAPAsyncSession::fetchMessageByNumberOperation(String * folder, uint32_t number, bool urgent) { IMAPFetchContentOperation * op = new IMAPFetchContentOperation(); diff --git a/src/async/imap/MCIMAPAsyncSession.h b/src/async/imap/MCIMAPAsyncSession.h index 0230deda..05437207 100644 --- a/src/async/imap/MCIMAPAsyncSession.h +++ b/src/async/imap/MCIMAPAsyncSession.h @@ -24,6 +24,7 @@ namespace mailcore { class IMAPMoveMessagesOperation; class IMAPFetchMessagesOperation; class IMAPFetchContentOperation; + class IMAPFetchContentToFileOperation; class IMAPFetchParsedContentOperation; class IMAPIdleOperation; class IMAPFolderInfoOperation; @@ -142,7 +143,13 @@ namespace mailcore { virtual IMAPFetchContentOperation * fetchMessageByUIDOperation(String * folder, uint32_t uid, bool urgent = false); virtual IMAPFetchContentOperation * fetchMessageAttachmentByUIDOperation(String * folder, uint32_t uid, String * partID, Encoding encoding, bool urgent = false); - + + virtual IMAPFetchContentToFileOperation * fetchMessageAttachmentToFileByUIDOperation( + String * folder, uint32_t uid, String * partID, + Encoding encoding, + String * filename, + bool urgent = false); + virtual IMAPFetchContentOperation * fetchMessageByNumberOperation(String * folder, uint32_t number, bool urgent = false); virtual IMAPCustomCommandOperation * customCommand(String *command, bool urgent); virtual IMAPFetchContentOperation * fetchMessageAttachmentByNumberOperation(String * folder, uint32_t number, String * partID, diff --git a/src/async/imap/MCIMAPFetchContentToFileOperation.cpp b/src/async/imap/MCIMAPFetchContentToFileOperation.cpp new file mode 100644 index 00000000..82a1fa23 --- /dev/null +++ b/src/async/imap/MCIMAPFetchContentToFileOperation.cpp @@ -0,0 +1,91 @@ +// +// IMAPFetchContentToFileOperation.cpp +// mailcore2 +// +// Created by Dmitry Isaikin on 2/08/16. +// Copyright (c) 2016 MailCore. All rights reserved. +// + +#include "MCIMAPFetchContentToFileOperation.h" + +#include "MCIMAPSession.h" +#include "MCIMAPAsyncConnection.h" + +using namespace mailcore; + +IMAPFetchContentToFileOperation::IMAPFetchContentToFileOperation() +{ + mUid = 0; + mPartID = NULL; + mEncoding = Encoding7Bit; + mFilename = NULL; + mLoadingByChunksEnabled = true; + mChunksSize = 2*1024*1024; + mEstimatedSize = 0; +} + +IMAPFetchContentToFileOperation::~IMAPFetchContentToFileOperation() +{ + MC_SAFE_RELEASE(mPartID); + MC_SAFE_RELEASE(mFilename); +} + +void IMAPFetchContentToFileOperation::setUid(uint32_t uid) +{ + mUid = uid; +} + +void IMAPFetchContentToFileOperation::setPartID(String * partID) +{ + MC_SAFE_REPLACE_COPY(String, mPartID, partID); +} + +void IMAPFetchContentToFileOperation::setEncoding(Encoding encoding) +{ + mEncoding = encoding; +} + +void IMAPFetchContentToFileOperation::setFilename(String * filename) +{ + MC_SAFE_REPLACE_COPY(String, mFilename, filename); +} + +void IMAPFetchContentToFileOperation::setLoadingByChunksEnabled(bool loadingByChunksEnabled) +{ + mLoadingByChunksEnabled = loadingByChunksEnabled; +} + +bool IMAPFetchContentToFileOperation::isLoadingByChunksEnabled() +{ + return mLoadingByChunksEnabled; +} + +void IMAPFetchContentToFileOperation::setChunksSize(uint32_t chunksSize) +{ + mChunksSize = chunksSize; +} + +uint32_t IMAPFetchContentToFileOperation::chunksSize() +{ + return mChunksSize; +} + +void IMAPFetchContentToFileOperation::setEstimatedSize(uint32_t estimatedSize) +{ + mEstimatedSize = estimatedSize; +} + +uint32_t IMAPFetchContentToFileOperation::estimatedSize() +{ + return mEstimatedSize; +} + +void IMAPFetchContentToFileOperation::main() +{ + ErrorCode error = ErrorNone; + session()->session()->fetchMessageAttachmentToFileByUID(folder(), mUid, mPartID, + mEstimatedSize, mEncoding, + mFilename, mChunksSize, + this, &error); + setError(error); +} diff --git a/src/async/imap/MCIMAPFetchContentToFileOperation.h b/src/async/imap/MCIMAPFetchContentToFileOperation.h new file mode 100644 index 00000000..35d7026f --- /dev/null +++ b/src/async/imap/MCIMAPFetchContentToFileOperation.h @@ -0,0 +1,53 @@ +// +// IMAPFetchContentToFileOperation.h +// mailcore2 +// +// Created by Dmitry Isaikin on 2/08/16. +// Copyright (c) 2016 MailCore. All rights reserved. +// + +#ifndef MAILCORE_IMAPFETCHCONTENTTOFILEOPERATION_H + +#define MAILCORE_IMAPFETCHCONTENTTOFILEOPERATION_H + +#include + +#ifdef __cplusplus + +namespace mailcore { + + class MAILCORE_EXPORT IMAPFetchContentToFileOperation : public IMAPOperation { + public: + IMAPFetchContentToFileOperation(); + virtual ~IMAPFetchContentToFileOperation(); + + virtual void setUid(uint32_t uid); + virtual void setPartID(String * partID); + virtual void setEncoding(Encoding encoding); + virtual void setFilename(String * filename); + + virtual void setLoadingByChunksEnabled(bool loadingByChunksEnabled); + virtual bool isLoadingByChunksEnabled(); + virtual void setChunksSize(uint32_t chunksSize); + virtual uint32_t chunksSize(); + virtual void setEstimatedSize(uint32_t estimatedSize); + virtual uint32_t estimatedSize(); + + public: // subclass behavior + virtual void main(); + + private: + uint32_t mUid; + String * mPartID; + Encoding mEncoding; + String * mFilename; + bool mLoadingByChunksEnabled; + uint32_t mChunksSize; + uint32_t mEstimatedSize; + }; + +} + +#endif + +#endif diff --git a/src/cmake/async.cmake b/src/cmake/async.cmake index 1c213a33..b71068fd 100644 --- a/src/cmake/async.cmake +++ b/src/cmake/async.cmake @@ -14,6 +14,7 @@ set(async_imap_files async/imap/MCIMAPDisconnectOperation.cpp async/imap/MCIMAPExpungeOperation.cpp async/imap/MCIMAPFetchContentOperation.cpp + async/imap/MCIMAPFetchContentToFileOperation.cpp async/imap/MCIMAPFetchParsedContentOperation.cpp async/imap/MCIMAPFetchFoldersOperation.cpp async/imap/MCIMAPFetchMessagesOperation.cpp diff --git a/src/cmake/core.cmake b/src/cmake/core.cmake index 963b02ce..63a041fc 100644 --- a/src/cmake/core.cmake +++ b/src/cmake/core.cmake @@ -43,6 +43,8 @@ set(basetypes_files core/basetypes/MCBase64.c core/basetypes/MCConnectionLoggerUtils.cpp core/basetypes/MCData.cpp + core/basetypes/MCDataDecoderUtils.cpp + core/basetypes/MCDataStreamDecoder.cpp core/basetypes/MCHash.cpp core/basetypes/MCHashMap.cpp core/basetypes/MCHTMLCleaner.cpp diff --git a/src/cmake/objc.cmake b/src/cmake/objc.cmake index f4e10b28..7b272111 100644 --- a/src/cmake/objc.cmake +++ b/src/cmake/objc.cmake @@ -17,6 +17,7 @@ set(objc_imap_files objc/imap/MCOIMAPCopyMessagesOperation.mm objc/imap/MCOIMAPMoveMessagesOperation.mm objc/imap/MCOIMAPFetchContentOperation.mm + objc/imap/MCOIMAPFetchContentToFileOperation.mm objc/imap/MCOIMAPFetchParsedContentOperation.mm objc/imap/MCOIMAPFetchFoldersOperation.mm objc/imap/MCOIMAPFetchMessagesOperation.mm diff --git a/src/cmake/public-headers.cmake b/src/cmake/public-headers.cmake index a6bd7f93..2907f9cc 100644 --- a/src/cmake/public-headers.cmake +++ b/src/cmake/public-headers.cmake @@ -12,6 +12,8 @@ core/basetypes/MCString.h core/basetypes/MCRange.h core/basetypes/MCICUTypes.h core/basetypes/MCData.h +core/basetypes/MCDataDecoderUtils.h +core/basetypes/MCDataStreamDecoder.h core/abstract/MCMessageConstants.h core/basetypes/MCArray.h core/basetypes/MCHashMap.h @@ -89,6 +91,7 @@ async/imap/MCIMAPCopyMessagesOperation.h async/imap/MCIMAPMoveMessagesOperation.h async/imap/MCIMAPFetchMessagesOperation.h async/imap/MCIMAPFetchContentOperation.h +async/imap/MCIMAPFetchContentToFileOperation.h async/imap/MCIMAPFetchParsedContentOperation.h async/imap/MCIMAPIdleOperation.h async/imap/MCIMAPFolderInfo.h @@ -167,6 +170,7 @@ objc/imap/MCOIMAPCopyMessagesOperation.h objc/imap/MCOIMAPMoveMessagesOperation.h objc/imap/MCOIMAPFetchMessagesOperation.h objc/imap/MCOIMAPFetchContentOperation.h +objc/imap/MCOIMAPFetchContentToFileOperation.h objc/imap/MCOIMAPFetchParsedContentOperation.h objc/imap/MCOIMAPSearchOperation.h objc/imap/MCOIMAPIdleOperation.h 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 + +#include +#include + +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 +#include + +#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 + +#include +#include +#include + +#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); diff --git a/src/objc/imap/MCOIMAP.h b/src/objc/imap/MCOIMAP.h index 11cbc2ae..4de8f18e 100644 --- a/src/objc/imap/MCOIMAP.h +++ b/src/objc/imap/MCOIMAP.h @@ -30,6 +30,7 @@ #import #import #import +#import #import #import #import diff --git a/src/objc/imap/MCOIMAPFetchContentToFileOperation.h b/src/objc/imap/MCOIMAPFetchContentToFileOperation.h new file mode 100644 index 00000000..4d06e94d --- /dev/null +++ b/src/objc/imap/MCOIMAPFetchContentToFileOperation.h @@ -0,0 +1,51 @@ +// +// MCOIMAPFetchContentToFileOperation.h +// mailcore2 +// +// Created by Dmitry Isaikin on 2/08/16. +// Copyright (c) 2016 MailCore. All rights reserved. +// + +#ifndef MAILCORE_MCOIMAPFETCHCONTENTTOFILEOPERATION_H + +#define MAILCORE_MCOIMAPFETCHCONTENTTOFILEOPERATION_H + +/** + This class implements an operation to fetch the content of a message to the file. + It can be a part or a full message. +*/ + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface MCOIMAPFetchContentToFileOperation : MCOIMAPBaseOperation + +/** + This block will be called as bytes are received from the network +*/ +@property (nonatomic, copy, nullable) MCOIMAPBaseOperationProgressBlock progress; + +@property (nonatomic, assign, getter=isLoadingByChunksEnabled) BOOL loadingByChunksEnabled; +@property (nonatomic, assign) uint32_t chunksSize; +@property (nonatomic, assign) uint32_t estimatedSize; + +/** + Starts the asynchronous fetch operation. + + @param completionBlock Called when the operation is finished. + + - On success `error` will be nil + + - On failure, `error` will be set with `MCOErrorDomain` as domain and an + error code available in `MCOConstants.h` +*/ + +- (void) start:(void (^)(NSError * __nullable error))completionBlock; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/src/objc/imap/MCOIMAPFetchContentToFileOperation.mm b/src/objc/imap/MCOIMAPFetchContentToFileOperation.mm new file mode 100644 index 00000000..4f2bbfcf --- /dev/null +++ b/src/objc/imap/MCOIMAPFetchContentToFileOperation.mm @@ -0,0 +1,103 @@ +// +// MCOIMAPFetchContentToFileOperation.mm +// mailcore2 +// +// Created by Dmitry Isaikin on 2/08/16. +// Copyright (c) 2016 MailCore. All rights reserved. +// + +#import "MCOIMAPFetchContentToFileOperation.h" + +#include "MCAsyncIMAP.h" + +#import "MCOOperation+Private.h" +#import "MCOUtils.h" + +typedef void (^CompletionType)(NSError *error); + +@implementation MCOIMAPFetchContentToFileOperation { + CompletionType _completionBlock; + MCOIMAPBaseOperationProgressBlock _progress; +} + +@synthesize progress = _progress; + +#define nativeType mailcore::IMAPFetchContentToFileOperation + ++ (void) load +{ + MCORegisterClass(self, &typeid(nativeType)); +} + ++ (NSObject *) mco_objectWithMCObject:(mailcore::Object *)object +{ + nativeType * op = (nativeType *) object; + return [[[self alloc] initWithMCOperation:op] autorelease]; +} + +- (void)setLoadingByChunksEnabled:(BOOL)loadingByChunksEnabled { + MCO_NATIVE_INSTANCE->setLoadingByChunksEnabled(loadingByChunksEnabled); +} + +- (BOOL)isLoadingByChunksEnabled { + return MCO_NATIVE_INSTANCE->isLoadingByChunksEnabled(); +} + +- (void)setChunksSize:(uint32_t)chunksSize { + MCO_NATIVE_INSTANCE->setChunksSize(chunksSize); +} + +- (uint32_t)chunksSize { + return MCO_NATIVE_INSTANCE->chunksSize(); +} + +- (void)setEstimatedSize:(uint32_t)estimatedSize { + MCO_NATIVE_INSTANCE->setEstimatedSize(estimatedSize); +} + +- (uint32_t)estimatedSize { + return MCO_NATIVE_INSTANCE->estimatedSize(); +} + +- (void) dealloc +{ + [_progress release]; + [_completionBlock release]; + [super dealloc]; +} + +- (void) start:(void (^)(NSError *error))completionBlock { + _completionBlock = [completionBlock copy]; + [self start]; +} + +- (void) cancel +{ + [_completionBlock release]; + _completionBlock = nil; + [super cancel]; +} + +- (void) operationCompleted +{ + if (_completionBlock == NULL) + return; + + nativeType *op = MCO_NATIVE_INSTANCE; + if (op->error() == mailcore::ErrorNone) { + _completionBlock(nil); + } else { + _completionBlock([NSError mco_errorWithErrorCode:op->error()]); + } + [_completionBlock release]; + _completionBlock = nil; +} + +- (void) bodyProgress:(unsigned int)current maximum:(unsigned int)maximum +{ + if (_progress != NULL) { + _progress(current, maximum); + } +} + +@end diff --git a/src/objc/imap/MCOIMAPSession.h b/src/objc/imap/MCOIMAPSession.h index 7674e9db..12a1e651 100644 --- a/src/objc/imap/MCOIMAPSession.h +++ b/src/objc/imap/MCOIMAPSession.h @@ -24,6 +24,7 @@ @class MCOIndexSet; @class MCOIMAPFetchMessagesOperation; @class MCOIMAPFetchContentOperation; +@class MCOIMAPFetchContentToFileOperation; @class MCOIMAPFetchParsedContentOperation; @class MCOIMAPSearchOperation; @class MCOIMAPIdleOperation; @@ -848,6 +849,37 @@ vanishedMessages will be set only for servers that support QRESYNC. See [RFC5162 partID:(NSString *)partID encoding:(MCOEncoding)encoding; +/** + Returns an operation to fetch an attachment to a file. + @param urgent is set to YES, an additional connection to the same folder might be opened to fetch the content. + Operation will be perform in a memory efficient manner. + + MCOIMAPFetchContentToFileOperation * op = [session fetchMessageAttachmentToFileOperationWithFolder:@"INBOX" + uid:456 + partID:@"1.2" + encoding:MCOEncodingBase64 + filename:filename + urgent:YES]; + + // Optionally, explicitly enable chunked mode + [op setLoadingByChunksEnabled:YES]; + [op setChunksSize:1024*1024]; + // need in chunked mode for correct progress indication + [op setEstimatedSize:sizeOfAttachFromBodystructure]; + + [op start:^(NSError * __nullable error) { + ... + }]; + + */ +- (MCOIMAPFetchContentToFileOperation *) fetchMessageAttachmentToFileOperationWithFolder:(NSString *)folder + uid:(uint32_t)uid + partID:(NSString *)partID + encoding:(MCOEncoding)encoding + filename:(NSString *)filename + urgent:(BOOL)urgent; + + /** @name General IMAP Actions */ /** diff --git a/src/objc/imap/MCOIMAPSession.mm b/src/objc/imap/MCOIMAPSession.mm index 2eba5cc0..94a3020b 100644 --- a/src/objc/imap/MCOIMAPSession.mm +++ b/src/objc/imap/MCOIMAPSession.mm @@ -498,6 +498,22 @@ MCO_OBJC_SYNTHESIZE_SCALAR(dispatch_queue_t, dispatch_queue_t, setDispatchQueue, return [self fetchMessageAttachmentOperationWithFolder:folder number:number partID:partID encoding:encoding urgent:NO]; } +- (MCOIMAPFetchContentToFileOperation *) fetchMessageAttachmentToFileOperationWithFolder:(NSString *)folder + uid:(uint32_t)uid + partID:(NSString *)partID + encoding:(MCOEncoding)encoding + filename:(NSString *)filename + urgent:(BOOL)urgent +{ + IMAPFetchContentToFileOperation * coreOp = MCO_NATIVE_INSTANCE->fetchMessageAttachmentToFileByUIDOperation([folder mco_mcString], + uid, + [partID mco_mcString], + (Encoding) encoding, + [filename mco_mcString], + urgent); + return MCO_TO_OBJC_OP(coreOp); +} + - (MCOIMAPOperation *) storeFlagsOperationWithFolder:(NSString *)folder uids:(MCOIndexSet *)uids kind:(MCOIMAPStoreFlagsRequestKind)kind -- cgit v1.2.3