diff options
111 files changed, 15947 insertions, 0 deletions
diff --git a/build-mac/mailcore2.xcodeproj/project.pbxproj b/build-mac/mailcore2.xcodeproj/project.pbxproj new file mode 100644 index 00000000..13472734 --- /dev/null +++ b/build-mac/mailcore2.xcodeproj/project.pbxproj @@ -0,0 +1,872 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + C64EA6F4169E847800778456 /* MCAbstractMessage.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA694169E847800778456 /* MCAbstractMessage.cc */; }; + C64EA6F6169E847800778456 /* MCAbstractMessagePart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA696169E847800778456 /* MCAbstractMessagePart.cc */; }; + C64EA6F8169E847800778456 /* MCAbstractMultipart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA698169E847800778456 /* MCAbstractMultipart.cc */; }; + C64EA6FA169E847800778456 /* MCAbstractPart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA69A169E847800778456 /* MCAbstractPart.cc */; }; + C64EA6FC169E847800778456 /* MCAddress.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA69C169E847800778456 /* MCAddress.cc */; }; + C64EA6FF169E847800778456 /* MCMessageHeader.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA69F169E847800778456 /* MCMessageHeader.cc */; }; + C64EA701169E847800778456 /* MCAutoreleasePool.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6A2169E847800778456 /* MCAutoreleasePool.cc */; }; + C64EA704169E847800778456 /* MCArray.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6A5169E847800778456 /* MCArray.cc */; }; + C64EA706169E847800778456 /* MCAssert.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6A7169E847800778456 /* MCAssert.cc */; }; + C64EA708169E847800778456 /* MCData.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6A9169E847800778456 /* MCData.cc */; }; + C64EA70A169E847800778456 /* MCHash.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6AB169E847800778456 /* MCHash.cc */; }; + C64EA70C169E847800778456 /* MCHashMap.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6AD169E847800778456 /* MCHashMap.cc */; }; + C64EA70E169E847800778456 /* MCLog.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6AF169E847800778456 /* MCLog.cc */; }; + C64EA710169E847800778456 /* MCObject.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6B1169E847800778456 /* MCObject.cc */; }; + C64EA712169E847800778456 /* MCRange.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6B3169E847800778456 /* MCRange.cc */; }; + C64EA714169E847800778456 /* MCSet.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6B5169E847800778456 /* MCSet.cc */; }; + C64EA716169E847800778456 /* MCString.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6B7169E847800778456 /* MCString.cc */; }; + C64EA719169E847800778456 /* MCValue.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6BA169E847800778456 /* MCValue.cc */; }; + C64EA71C169E847800778456 /* MCMainThread.mm in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6BD169E847800778456 /* MCMainThread.mm */; }; + C64EA71D169E847800778456 /* MCOperation.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6BE169E847800778456 /* MCOperation.cc */; }; + C64EA720169E847800778456 /* MCOperationQueue.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6C1169E847800778456 /* MCOperationQueue.cc */; }; + C64EA723169E847800778456 /* MCIMAPFolder.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6C5169E847800778456 /* MCIMAPFolder.cc */; }; + C64EA725169E847800778456 /* MCIMAPMessage.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6C7169E847800778456 /* MCIMAPMessage.cc */; }; + C64EA727169E847800778456 /* MCIMAPMessagePart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6C9169E847800778456 /* MCIMAPMessagePart.cc */; }; + C64EA729169E847800778456 /* MCIMAPMultipart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6CB169E847800778456 /* MCIMAPMultipart.cc */; }; + C64EA72B169E847800778456 /* MCIMAPNamespace.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6CD169E847800778456 /* MCIMAPNamespace.cc */; }; + C64EA72D169E847800778456 /* MCIMAPNamespaceItem.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6CF169E847800778456 /* MCIMAPNamespaceItem.cc */; }; + C64EA72F169E847800778456 /* MCIMAPPart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6D1169E847800778456 /* MCIMAPPart.cc */; }; + C64EA732169E847800778456 /* MCIMAPSearchExpression.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6D4169E847800778456 /* MCIMAPSearchExpression.cc */; }; + C64EA734169E847800778456 /* MCIMAPSession.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6D6169E847800778456 /* MCIMAPSession.cc */; }; + C64EA737169E847800778456 /* MCPOPMessageInfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6DA169E847800778456 /* MCPOPMessageInfo.cc */; }; + C64EA73A169E847800778456 /* MCPOPSession.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6DD169E847800778456 /* MCPOPSession.cc */; }; + C64EA73C169E847800778456 /* MCAttachment.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6E0169E847800778456 /* MCAttachment.cc */; }; + C64EA73E169E847800778456 /* MCMessageBuilder.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6E2169E847800778456 /* MCMessageBuilder.cc */; }; + C64EA740169E847800778456 /* MCMessageParser.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6E4169E847800778456 /* MCMessageParser.cc */; }; + C64EA742169E847800778456 /* MCMessagePart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6E6169E847800778456 /* MCMessagePart.cc */; }; + C64EA744169E847800778456 /* MCMultipart.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6E8169E847800778456 /* MCMultipart.cc */; }; + C64EA74C169E859600778456 /* IMAPAsyncSession.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA68C169E847800778456 /* IMAPAsyncSession.h */; }; + C64EA74D169E859600778456 /* MCSMTPAsyncSession.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA690169E847800778456 /* MCSMTPAsyncSession.h */; }; + C64EA74E169E859600778456 /* MCAbstract.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA693169E847800778456 /* MCAbstract.h */; }; + C64EA74F169E859600778456 /* MCAbstractMessage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA695169E847800778456 /* MCAbstractMessage.h */; }; + C64EA750169E859600778456 /* MCAbstractMessagePart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA697169E847800778456 /* MCAbstractMessagePart.h */; }; + C64EA751169E859600778456 /* MCAbstractMultipart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA699169E847800778456 /* MCAbstractMultipart.h */; }; + C64EA752169E859600778456 /* MCAbstractPart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA69B169E847800778456 /* MCAbstractPart.h */; }; + C64EA753169E859600778456 /* MCAddress.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA69D169E847800778456 /* MCAddress.h */; }; + C64EA754169E859600778456 /* MCMessageConstants.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA69E169E847800778456 /* MCMessageConstants.h */; }; + C64EA755169E859600778456 /* MCMessageHeader.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6A0169E847800778456 /* MCMessageHeader.h */; }; + C64EA756169E859600778456 /* MCAutoreleasePool.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6A3169E847800778456 /* MCAutoreleasePool.h */; }; + C64EA757169E859600778456 /* MCBaseTypes.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6A4169E847800778456 /* MCBaseTypes.h */; }; + C64EA758169E859600778456 /* MCArray.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6A6169E847800778456 /* MCArray.h */; }; + C64EA759169E859600778456 /* MCAssert.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6A8169E847800778456 /* MCAssert.h */; }; + C64EA75A169E859600778456 /* MCData.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6AA169E847800778456 /* MCData.h */; }; + C64EA75B169E859600778456 /* MCHash.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6AC169E847800778456 /* MCHash.h */; }; + C64EA75C169E859600778456 /* MCHashMap.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6AE169E847800778456 /* MCHashMap.h */; }; + C64EA75D169E859600778456 /* MCLog.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6B0169E847800778456 /* MCLog.h */; }; + C64EA75E169E859600778456 /* MCObject.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6B2169E847800778456 /* MCObject.h */; }; + C64EA75F169E859600778456 /* MCRange.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6B4169E847800778456 /* MCRange.h */; }; + C64EA760169E859600778456 /* MCSet.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6B6169E847800778456 /* MCSet.h */; }; + C64EA761169E859600778456 /* MCString.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6B8169E847800778456 /* MCString.h */; }; + C64EA762169E859600778456 /* MCUtils.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6B9169E847800778456 /* MCUtils.h */; }; + C64EA763169E859600778456 /* MCValue.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6BB169E847800778456 /* MCValue.h */; }; + C64EA764169E859600778456 /* MCMainThread.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6BC169E847800778456 /* MCMainThread.h */; }; + C64EA765169E859600778456 /* MCOperation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6BF169E847800778456 /* MCOperation.h */; }; + C64EA766169E859600778456 /* MCOperationCallback.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6C0169E847800778456 /* MCOperationCallback.h */; }; + C64EA767169E859600778456 /* MCOperationQueue.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6C2169E847800778456 /* MCOperationQueue.h */; }; + C64EA768169E859600778456 /* MCIMAP.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6C4169E847800778456 /* MCIMAP.h */; }; + C64EA769169E859600778456 /* MCIMAPFolder.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6C6169E847800778456 /* MCIMAPFolder.h */; }; + C64EA76A169E859600778456 /* MCIMAPMessage.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6C8169E847800778456 /* MCIMAPMessage.h */; }; + C64EA76B169E859600778456 /* MCIMAPMessagePart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6CA169E847800778456 /* MCIMAPMessagePart.h */; }; + C64EA76C169E859600778456 /* MCIMAPMultipart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6CC169E847800778456 /* MCIMAPMultipart.h */; }; + C64EA76D169E859600778456 /* MCIMAPNamespace.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6CE169E847800778456 /* MCIMAPNamespace.h */; }; + C64EA76E169E859600778456 /* MCIMAPNamespaceItem.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6D0169E847800778456 /* MCIMAPNamespaceItem.h */; }; + C64EA76F169E859600778456 /* MCIMAPPart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6D2169E847800778456 /* MCIMAPPart.h */; }; + C64EA770169E859600778456 /* MCIMAPProgressCallback.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6D3169E847800778456 /* MCIMAPProgressCallback.h */; }; + C64EA771169E859600778456 /* MCIMAPSearchExpression.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6D5169E847800778456 /* MCIMAPSearchExpression.h */; }; + C64EA772169E859600778456 /* MCIMAPSession.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6D7169E847800778456 /* MCIMAPSession.h */; }; + C64EA773169E859600778456 /* MCPOP.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6D9169E847800778456 /* MCPOP.h */; }; + C64EA774169E859600778456 /* MCPOPMessageInfo.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6DB169E847800778456 /* MCPOPMessageInfo.h */; }; + C64EA775169E859600778456 /* MCPOPProgressCallback.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6DC169E847800778456 /* MCPOPProgressCallback.h */; }; + C64EA776169E859600778456 /* MCPOPSession.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6DE169E847800778456 /* MCPOPSession.h */; }; + C64EA777169E859600778456 /* MCAttachment.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6E1169E847800778456 /* MCAttachment.h */; }; + C64EA778169E859600778456 /* MCMessageBuilder.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6E3169E847800778456 /* MCMessageBuilder.h */; }; + C64EA779169E859600778456 /* MCMessageParser.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6E5169E847800778456 /* MCMessageParser.h */; }; + C64EA77A169E859600778456 /* MCMessagePart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6E7169E847800778456 /* MCMessagePart.h */; }; + C64EA77B169E859600778456 /* MCMultipart.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6E9169E847800778456 /* MCMultipart.h */; }; + C64EA77C169E859600778456 /* MCRFC822.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6EA169E847800778456 /* MCRFC822.h */; }; + C64EA77D169E859600778456 /* MCSMTP.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6EC169E847800778456 /* MCSMTP.h */; }; + C64EA77E169E859600778456 /* MCSMTPProgressCallback.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6ED169E847800778456 /* MCSMTPProgressCallback.h */; }; + C64EA77F169E859600778456 /* MCSMTPSession.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA6EF169E847800778456 /* MCSMTPSession.h */; }; + C64EA781169E89F600778456 /* MCSMTPSession.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA6EE169E847800778456 /* MCSMTPSession.cc */; }; + C64EA783169F241300778456 /* MCCore.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA782169F23AB00778456 /* MCCore.h */; }; + C64EA784169F24E400778456 /* MCSMTPAsyncSession.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA68F169E847800778456 /* MCSMTPAsyncSession.cc */; }; + C64EA787169F252C00778456 /* MCSMTPSendWithRecipientOperation.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA785169F252C00778456 /* MCSMTPSendWithRecipientOperation.cc */; }; + C64EA790169F259200778456 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C64EA78F169F259200778456 /* Foundation.framework */; }; + C64EA79B169F259F00778456 /* libmailcore2.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C64EA537169E772200778456 /* libmailcore2.a */; }; + C64EA79E169F29A700778456 /* MCSMTPSendWithDataOperation.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA79C169F29A700778456 /* MCSMTPSendWithDataOperation.cc */; }; + C64EA7A1169F29D900778456 /* MCSMTPSendWithBuilderOperation.cc in Sources */ = {isa = PBXBuildFile; fileRef = C64EA79F169F29D900778456 /* MCSMTPSendWithBuilderOperation.cc */; }; + C64EA7A5169F2A6100778456 /* mailcore.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = C64EA7A4169F2A3E00778456 /* mailcore.h */; }; + C64EA7AB16A00AF600778456 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = C64EA7AA16A00AF600778456 /* main.mm */; }; + C64EA7B116A00BBB00778456 /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C64EA7B016A00BBB00778456 /* CoreServices.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C64EA7A2169F2A1D00778456 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = C64EA52E169E772200778456 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C64EA536169E772200778456; + remoteInfo = mailcore2; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + C64EA74B169E854B00778456 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = include/mailcore; + dstSubfolderSpec = 16; + files = ( + C64EA74C169E859600778456 /* IMAPAsyncSession.h in CopyFiles */, + C64EA74D169E859600778456 /* MCSMTPAsyncSession.h in CopyFiles */, + C64EA74E169E859600778456 /* MCAbstract.h in CopyFiles */, + C64EA74F169E859600778456 /* MCAbstractMessage.h in CopyFiles */, + C64EA750169E859600778456 /* MCAbstractMessagePart.h in CopyFiles */, + C64EA751169E859600778456 /* MCAbstractMultipart.h in CopyFiles */, + C64EA752169E859600778456 /* MCAbstractPart.h in CopyFiles */, + C64EA7A5169F2A6100778456 /* mailcore.h in CopyFiles */, + C64EA753169E859600778456 /* MCAddress.h in CopyFiles */, + C64EA754169E859600778456 /* MCMessageConstants.h in CopyFiles */, + C64EA755169E859600778456 /* MCMessageHeader.h in CopyFiles */, + C64EA756169E859600778456 /* MCAutoreleasePool.h in CopyFiles */, + C64EA757169E859600778456 /* MCBaseTypes.h in CopyFiles */, + C64EA758169E859600778456 /* MCArray.h in CopyFiles */, + C64EA759169E859600778456 /* MCAssert.h in CopyFiles */, + C64EA75A169E859600778456 /* MCData.h in CopyFiles */, + C64EA75B169E859600778456 /* MCHash.h in CopyFiles */, + C64EA75C169E859600778456 /* MCHashMap.h in CopyFiles */, + C64EA75D169E859600778456 /* MCLog.h in CopyFiles */, + C64EA75E169E859600778456 /* MCObject.h in CopyFiles */, + C64EA75F169E859600778456 /* MCRange.h in CopyFiles */, + C64EA760169E859600778456 /* MCSet.h in CopyFiles */, + C64EA761169E859600778456 /* MCString.h in CopyFiles */, + C64EA762169E859600778456 /* MCUtils.h in CopyFiles */, + C64EA763169E859600778456 /* MCValue.h in CopyFiles */, + C64EA783169F241300778456 /* MCCore.h in CopyFiles */, + C64EA764169E859600778456 /* MCMainThread.h in CopyFiles */, + C64EA765169E859600778456 /* MCOperation.h in CopyFiles */, + C64EA766169E859600778456 /* MCOperationCallback.h in CopyFiles */, + C64EA767169E859600778456 /* MCOperationQueue.h in CopyFiles */, + C64EA768169E859600778456 /* MCIMAP.h in CopyFiles */, + C64EA769169E859600778456 /* MCIMAPFolder.h in CopyFiles */, + C64EA76A169E859600778456 /* MCIMAPMessage.h in CopyFiles */, + C64EA76B169E859600778456 /* MCIMAPMessagePart.h in CopyFiles */, + C64EA76C169E859600778456 /* MCIMAPMultipart.h in CopyFiles */, + C64EA76D169E859600778456 /* MCIMAPNamespace.h in CopyFiles */, + C64EA76E169E859600778456 /* MCIMAPNamespaceItem.h in CopyFiles */, + C64EA76F169E859600778456 /* MCIMAPPart.h in CopyFiles */, + C64EA770169E859600778456 /* MCIMAPProgressCallback.h in CopyFiles */, + C64EA771169E859600778456 /* MCIMAPSearchExpression.h in CopyFiles */, + C64EA772169E859600778456 /* MCIMAPSession.h in CopyFiles */, + C64EA773169E859600778456 /* MCPOP.h in CopyFiles */, + C64EA774169E859600778456 /* MCPOPMessageInfo.h in CopyFiles */, + C64EA775169E859600778456 /* MCPOPProgressCallback.h in CopyFiles */, + C64EA776169E859600778456 /* MCPOPSession.h in CopyFiles */, + C64EA777169E859600778456 /* MCAttachment.h in CopyFiles */, + C64EA778169E859600778456 /* MCMessageBuilder.h in CopyFiles */, + C64EA779169E859600778456 /* MCMessageParser.h in CopyFiles */, + C64EA77A169E859600778456 /* MCMessagePart.h in CopyFiles */, + C64EA77B169E859600778456 /* MCMultipart.h in CopyFiles */, + C64EA77C169E859600778456 /* MCRFC822.h in CopyFiles */, + C64EA77D169E859600778456 /* MCSMTP.h in CopyFiles */, + C64EA77E169E859600778456 /* MCSMTPProgressCallback.h in CopyFiles */, + C64EA77F169E859600778456 /* MCSMTPSession.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C64EA78A169F259200778456 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + C64EA537169E772200778456 /* libmailcore2.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libmailcore2.a; sourceTree = BUILT_PRODUCTS_DIR; }; + C64EA68C169E847800778456 /* IMAPAsyncSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IMAPAsyncSession.h; sourceTree = "<group>"; }; + C64EA68F169E847800778456 /* MCSMTPAsyncSession.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCSMTPAsyncSession.cc; sourceTree = "<group>"; }; + C64EA690169E847800778456 /* MCSMTPAsyncSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSMTPAsyncSession.h; sourceTree = "<group>"; }; + C64EA693169E847800778456 /* MCAbstract.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAbstract.h; sourceTree = "<group>"; }; + C64EA694169E847800778456 /* MCAbstractMessage.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = MCAbstractMessage.cc; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + C64EA695169E847800778456 /* MCAbstractMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = MCAbstractMessage.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; + C64EA696169E847800778456 /* MCAbstractMessagePart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = MCAbstractMessagePart.cc; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + C64EA697169E847800778456 /* MCAbstractMessagePart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAbstractMessagePart.h; sourceTree = "<group>"; }; + C64EA698169E847800778456 /* MCAbstractMultipart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCAbstractMultipart.cc; sourceTree = "<group>"; }; + C64EA699169E847800778456 /* MCAbstractMultipart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAbstractMultipart.h; sourceTree = "<group>"; }; + C64EA69A169E847800778456 /* MCAbstractPart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCAbstractPart.cc; sourceTree = "<group>"; }; + C64EA69B169E847800778456 /* MCAbstractPart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAbstractPart.h; sourceTree = "<group>"; }; + C64EA69C169E847800778456 /* MCAddress.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCAddress.cc; sourceTree = "<group>"; }; + C64EA69D169E847800778456 /* MCAddress.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAddress.h; sourceTree = "<group>"; }; + C64EA69E169E847800778456 /* MCMessageConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCMessageConstants.h; sourceTree = "<group>"; }; + C64EA69F169E847800778456 /* MCMessageHeader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCMessageHeader.cc; sourceTree = "<group>"; }; + C64EA6A0169E847800778456 /* MCMessageHeader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCMessageHeader.h; sourceTree = "<group>"; }; + C64EA6A2169E847800778456 /* MCAutoreleasePool.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCAutoreleasePool.cc; sourceTree = "<group>"; }; + C64EA6A3169E847800778456 /* MCAutoreleasePool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAutoreleasePool.h; sourceTree = "<group>"; }; + C64EA6A4169E847800778456 /* MCBaseTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCBaseTypes.h; sourceTree = "<group>"; }; + C64EA6A5169E847800778456 /* MCArray.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCArray.cc; sourceTree = "<group>"; }; + C64EA6A6169E847800778456 /* MCArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCArray.h; sourceTree = "<group>"; }; + C64EA6A7169E847800778456 /* MCAssert.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCAssert.cc; sourceTree = "<group>"; }; + C64EA6A8169E847800778456 /* MCAssert.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAssert.h; sourceTree = "<group>"; }; + C64EA6A9169E847800778456 /* MCData.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCData.cc; sourceTree = "<group>"; }; + C64EA6AA169E847800778456 /* MCData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCData.h; sourceTree = "<group>"; }; + C64EA6AB169E847800778456 /* MCHash.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCHash.cc; sourceTree = "<group>"; }; + C64EA6AC169E847800778456 /* MCHash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCHash.h; sourceTree = "<group>"; }; + C64EA6AD169E847800778456 /* MCHashMap.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCHashMap.cc; sourceTree = "<group>"; }; + C64EA6AE169E847800778456 /* MCHashMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCHashMap.h; sourceTree = "<group>"; }; + C64EA6AF169E847800778456 /* MCLog.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCLog.cc; sourceTree = "<group>"; }; + C64EA6B0169E847800778456 /* MCLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCLog.h; sourceTree = "<group>"; }; + C64EA6B1169E847800778456 /* MCObject.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCObject.cc; sourceTree = "<group>"; }; + C64EA6B2169E847800778456 /* MCObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCObject.h; sourceTree = "<group>"; }; + C64EA6B3169E847800778456 /* MCRange.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCRange.cc; sourceTree = "<group>"; }; + C64EA6B4169E847800778456 /* MCRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCRange.h; sourceTree = "<group>"; }; + C64EA6B5169E847800778456 /* MCSet.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCSet.cc; sourceTree = "<group>"; }; + C64EA6B6169E847800778456 /* MCSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSet.h; sourceTree = "<group>"; }; + C64EA6B7169E847800778456 /* MCString.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCString.cc; sourceTree = "<group>"; }; + C64EA6B8169E847800778456 /* MCString.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCString.h; sourceTree = "<group>"; }; + C64EA6B9169E847800778456 /* MCUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCUtils.h; sourceTree = "<group>"; }; + C64EA6BA169E847800778456 /* MCValue.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCValue.cc; sourceTree = "<group>"; }; + C64EA6BB169E847800778456 /* MCValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCValue.h; sourceTree = "<group>"; }; + C64EA6BC169E847800778456 /* MCMainThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCMainThread.h; sourceTree = "<group>"; }; + C64EA6BD169E847800778456 /* MCMainThread.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MCMainThread.mm; sourceTree = "<group>"; }; + C64EA6BE169E847800778456 /* MCOperation.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCOperation.cc; sourceTree = "<group>"; }; + C64EA6BF169E847800778456 /* MCOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCOperation.h; sourceTree = "<group>"; }; + C64EA6C0169E847800778456 /* MCOperationCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCOperationCallback.h; sourceTree = "<group>"; }; + C64EA6C1169E847800778456 /* MCOperationQueue.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCOperationQueue.cc; sourceTree = "<group>"; }; + C64EA6C2169E847800778456 /* MCOperationQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCOperationQueue.h; sourceTree = "<group>"; }; + C64EA6C4169E847800778456 /* MCIMAP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAP.h; sourceTree = "<group>"; }; + C64EA6C5169E847800778456 /* MCIMAPFolder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPFolder.cc; sourceTree = "<group>"; }; + C64EA6C6169E847800778456 /* MCIMAPFolder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPFolder.h; sourceTree = "<group>"; }; + C64EA6C7169E847800778456 /* MCIMAPMessage.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPMessage.cc; sourceTree = "<group>"; }; + C64EA6C8169E847800778456 /* MCIMAPMessage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPMessage.h; sourceTree = "<group>"; }; + C64EA6C9169E847800778456 /* MCIMAPMessagePart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPMessagePart.cc; sourceTree = "<group>"; }; + C64EA6CA169E847800778456 /* MCIMAPMessagePart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPMessagePart.h; sourceTree = "<group>"; }; + C64EA6CB169E847800778456 /* MCIMAPMultipart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPMultipart.cc; sourceTree = "<group>"; }; + C64EA6CC169E847800778456 /* MCIMAPMultipart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPMultipart.h; sourceTree = "<group>"; }; + C64EA6CD169E847800778456 /* MCIMAPNamespace.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPNamespace.cc; sourceTree = "<group>"; }; + C64EA6CE169E847800778456 /* MCIMAPNamespace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPNamespace.h; sourceTree = "<group>"; }; + C64EA6CF169E847800778456 /* MCIMAPNamespaceItem.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPNamespaceItem.cc; sourceTree = "<group>"; }; + C64EA6D0169E847800778456 /* MCIMAPNamespaceItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPNamespaceItem.h; sourceTree = "<group>"; }; + C64EA6D1169E847800778456 /* MCIMAPPart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPPart.cc; sourceTree = "<group>"; }; + C64EA6D2169E847800778456 /* MCIMAPPart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPPart.h; sourceTree = "<group>"; }; + C64EA6D3169E847800778456 /* MCIMAPProgressCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPProgressCallback.h; sourceTree = "<group>"; }; + C64EA6D4169E847800778456 /* MCIMAPSearchExpression.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPSearchExpression.cc; sourceTree = "<group>"; }; + C64EA6D5169E847800778456 /* MCIMAPSearchExpression.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPSearchExpression.h; sourceTree = "<group>"; }; + C64EA6D6169E847800778456 /* MCIMAPSession.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCIMAPSession.cc; sourceTree = "<group>"; }; + C64EA6D7169E847800778456 /* MCIMAPSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCIMAPSession.h; sourceTree = "<group>"; }; + C64EA6D9169E847800778456 /* MCPOP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCPOP.h; sourceTree = "<group>"; }; + C64EA6DA169E847800778456 /* MCPOPMessageInfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCPOPMessageInfo.cc; sourceTree = "<group>"; }; + C64EA6DB169E847800778456 /* MCPOPMessageInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCPOPMessageInfo.h; sourceTree = "<group>"; }; + C64EA6DC169E847800778456 /* MCPOPProgressCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCPOPProgressCallback.h; sourceTree = "<group>"; }; + C64EA6DD169E847800778456 /* MCPOPSession.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCPOPSession.cc; sourceTree = "<group>"; }; + C64EA6DE169E847800778456 /* MCPOPSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCPOPSession.h; sourceTree = "<group>"; }; + C64EA6E0169E847800778456 /* MCAttachment.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCAttachment.cc; sourceTree = "<group>"; }; + C64EA6E1169E847800778456 /* MCAttachment.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCAttachment.h; sourceTree = "<group>"; }; + C64EA6E2169E847800778456 /* MCMessageBuilder.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCMessageBuilder.cc; sourceTree = "<group>"; }; + C64EA6E3169E847800778456 /* MCMessageBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCMessageBuilder.h; sourceTree = "<group>"; }; + C64EA6E4169E847800778456 /* MCMessageParser.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCMessageParser.cc; sourceTree = "<group>"; }; + C64EA6E5169E847800778456 /* MCMessageParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCMessageParser.h; sourceTree = "<group>"; }; + C64EA6E6169E847800778456 /* MCMessagePart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCMessagePart.cc; sourceTree = "<group>"; }; + C64EA6E7169E847800778456 /* MCMessagePart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCMessagePart.h; sourceTree = "<group>"; }; + C64EA6E8169E847800778456 /* MCMultipart.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCMultipart.cc; sourceTree = "<group>"; }; + C64EA6E9169E847800778456 /* MCMultipart.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCMultipart.h; sourceTree = "<group>"; }; + C64EA6EA169E847800778456 /* MCRFC822.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCRFC822.h; sourceTree = "<group>"; }; + C64EA6EC169E847800778456 /* MCSMTP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSMTP.h; sourceTree = "<group>"; }; + C64EA6ED169E847800778456 /* MCSMTPProgressCallback.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSMTPProgressCallback.h; sourceTree = "<group>"; }; + C64EA6EE169E847800778456 /* MCSMTPSession.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCSMTPSession.cc; sourceTree = "<group>"; }; + C64EA6EF169E847800778456 /* MCSMTPSession.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSMTPSession.h; sourceTree = "<group>"; }; + C64EA782169F23AB00778456 /* MCCore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCCore.h; sourceTree = "<group>"; }; + C64EA785169F252C00778456 /* MCSMTPSendWithRecipientOperation.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCSMTPSendWithRecipientOperation.cc; sourceTree = "<group>"; }; + C64EA786169F252C00778456 /* MCSMTPSendWithRecipientOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSMTPSendWithRecipientOperation.h; sourceTree = "<group>"; }; + C64EA78C169F259200778456 /* tests */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tests; sourceTree = BUILT_PRODUCTS_DIR; }; + C64EA78F169F259200778456 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + C64EA79C169F29A700778456 /* MCSMTPSendWithDataOperation.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCSMTPSendWithDataOperation.cc; sourceTree = "<group>"; }; + C64EA79D169F29A700778456 /* MCSMTPSendWithDataOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSMTPSendWithDataOperation.h; sourceTree = "<group>"; }; + C64EA79F169F29D900778456 /* MCSMTPSendWithBuilderOperation.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MCSMTPSendWithBuilderOperation.cc; sourceTree = "<group>"; }; + C64EA7A0169F29D900778456 /* MCSMTPSendWithBuilderOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSMTPSendWithBuilderOperation.h; sourceTree = "<group>"; }; + C64EA7A4169F2A3E00778456 /* mailcore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mailcore.h; sourceTree = "<group>"; }; + C64EA7AA16A00AF600778456 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; }; + C64EA7B016A00BBB00778456 /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C64EA534169E772200778456 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C64EA789169F259200778456 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C64EA7B116A00BBB00778456 /* CoreServices.framework in Frameworks */, + C64EA790169F259200778456 /* Foundation.framework in Frameworks */, + C64EA79B169F259F00778456 /* libmailcore2.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C64EA52C169E772200778456 = { + isa = PBXGroup; + children = ( + C64EA7B016A00BBB00778456 /* CoreServices.framework */, + C64EA545169E78B100778456 /* src */, + C64EA7A916A00AF400778456 /* tests */, + C64EA78E169F259200778456 /* Frameworks */, + C64EA538169E772200778456 /* Products */, + ); + sourceTree = "<group>"; + }; + C64EA538169E772200778456 /* Products */ = { + isa = PBXGroup; + children = ( + C64EA537169E772200778456 /* libmailcore2.a */, + C64EA78C169F259200778456 /* tests */, + ); + name = Products; + sourceTree = "<group>"; + }; + C64EA545169E78B100778456 /* src */ = { + isa = PBXGroup; + children = ( + C64EA68A169E847800778456 /* async */, + C64EA691169E847800778456 /* core */, + C64EA7A4169F2A3E00778456 /* mailcore.h */, + ); + name = src; + path = ../src; + sourceTree = "<group>"; + }; + C64EA68A169E847800778456 /* async */ = { + isa = PBXGroup; + children = ( + C64EA68B169E847800778456 /* imap */, + C64EA68D169E847800778456 /* pop */, + C64EA68E169E847800778456 /* smtp */, + ); + path = async; + sourceTree = "<group>"; + }; + C64EA68B169E847800778456 /* imap */ = { + isa = PBXGroup; + children = ( + C64EA68C169E847800778456 /* IMAPAsyncSession.h */, + ); + path = imap; + sourceTree = "<group>"; + }; + C64EA68D169E847800778456 /* pop */ = { + isa = PBXGroup; + children = ( + ); + path = pop; + sourceTree = "<group>"; + }; + C64EA68E169E847800778456 /* smtp */ = { + isa = PBXGroup; + children = ( + C64EA68F169E847800778456 /* MCSMTPAsyncSession.cc */, + C64EA690169E847800778456 /* MCSMTPAsyncSession.h */, + C64EA785169F252C00778456 /* MCSMTPSendWithRecipientOperation.cc */, + C64EA786169F252C00778456 /* MCSMTPSendWithRecipientOperation.h */, + C64EA79C169F29A700778456 /* MCSMTPSendWithDataOperation.cc */, + C64EA79D169F29A700778456 /* MCSMTPSendWithDataOperation.h */, + C64EA79F169F29D900778456 /* MCSMTPSendWithBuilderOperation.cc */, + C64EA7A0169F29D900778456 /* MCSMTPSendWithBuilderOperation.h */, + ); + path = smtp; + sourceTree = "<group>"; + }; + C64EA691169E847800778456 /* core */ = { + isa = PBXGroup; + children = ( + C64EA692169E847800778456 /* abstract */, + C64EA6A1169E847800778456 /* basetypes */, + C64EA6C3169E847800778456 /* imap */, + C64EA6D8169E847800778456 /* pop */, + C64EA6DF169E847800778456 /* rfc822 */, + C64EA6EB169E847800778456 /* smtp */, + C64EA782169F23AB00778456 /* MCCore.h */, + ); + path = core; + sourceTree = "<group>"; + }; + C64EA692169E847800778456 /* abstract */ = { + isa = PBXGroup; + children = ( + C64EA693169E847800778456 /* MCAbstract.h */, + C64EA694169E847800778456 /* MCAbstractMessage.cc */, + C64EA695169E847800778456 /* MCAbstractMessage.h */, + C64EA696169E847800778456 /* MCAbstractMessagePart.cc */, + C64EA697169E847800778456 /* MCAbstractMessagePart.h */, + C64EA698169E847800778456 /* MCAbstractMultipart.cc */, + C64EA699169E847800778456 /* MCAbstractMultipart.h */, + C64EA69A169E847800778456 /* MCAbstractPart.cc */, + C64EA69B169E847800778456 /* MCAbstractPart.h */, + C64EA69C169E847800778456 /* MCAddress.cc */, + C64EA69D169E847800778456 /* MCAddress.h */, + C64EA69E169E847800778456 /* MCMessageConstants.h */, + C64EA69F169E847800778456 /* MCMessageHeader.cc */, + C64EA6A0169E847800778456 /* MCMessageHeader.h */, + ); + path = abstract; + sourceTree = "<group>"; + }; + C64EA6A1169E847800778456 /* basetypes */ = { + isa = PBXGroup; + children = ( + C64EA6A2169E847800778456 /* MCAutoreleasePool.cc */, + C64EA6A3169E847800778456 /* MCAutoreleasePool.h */, + C64EA6A4169E847800778456 /* MCBaseTypes.h */, + C64EA6A5169E847800778456 /* MCArray.cc */, + C64EA6A6169E847800778456 /* MCArray.h */, + C64EA6A7169E847800778456 /* MCAssert.cc */, + C64EA6A8169E847800778456 /* MCAssert.h */, + C64EA6A9169E847800778456 /* MCData.cc */, + C64EA6AA169E847800778456 /* MCData.h */, + C64EA6AB169E847800778456 /* MCHash.cc */, + C64EA6AC169E847800778456 /* MCHash.h */, + C64EA6AD169E847800778456 /* MCHashMap.cc */, + C64EA6AE169E847800778456 /* MCHashMap.h */, + C64EA6AF169E847800778456 /* MCLog.cc */, + C64EA6B0169E847800778456 /* MCLog.h */, + C64EA6B1169E847800778456 /* MCObject.cc */, + C64EA6B2169E847800778456 /* MCObject.h */, + C64EA6B3169E847800778456 /* MCRange.cc */, + C64EA6B4169E847800778456 /* MCRange.h */, + C64EA6B5169E847800778456 /* MCSet.cc */, + C64EA6B6169E847800778456 /* MCSet.h */, + C64EA6B7169E847800778456 /* MCString.cc */, + C64EA6B8169E847800778456 /* MCString.h */, + C64EA6B9169E847800778456 /* MCUtils.h */, + C64EA6BA169E847800778456 /* MCValue.cc */, + C64EA6BB169E847800778456 /* MCValue.h */, + C64EA6BC169E847800778456 /* MCMainThread.h */, + C64EA6BD169E847800778456 /* MCMainThread.mm */, + C64EA6BE169E847800778456 /* MCOperation.cc */, + C64EA6BF169E847800778456 /* MCOperation.h */, + C64EA6C0169E847800778456 /* MCOperationCallback.h */, + C64EA6C1169E847800778456 /* MCOperationQueue.cc */, + C64EA6C2169E847800778456 /* MCOperationQueue.h */, + ); + path = basetypes; + sourceTree = "<group>"; + }; + C64EA6C3169E847800778456 /* imap */ = { + isa = PBXGroup; + children = ( + C64EA6C4169E847800778456 /* MCIMAP.h */, + C64EA6C5169E847800778456 /* MCIMAPFolder.cc */, + C64EA6C6169E847800778456 /* MCIMAPFolder.h */, + C64EA6C7169E847800778456 /* MCIMAPMessage.cc */, + C64EA6C8169E847800778456 /* MCIMAPMessage.h */, + C64EA6C9169E847800778456 /* MCIMAPMessagePart.cc */, + C64EA6CA169E847800778456 /* MCIMAPMessagePart.h */, + C64EA6CB169E847800778456 /* MCIMAPMultipart.cc */, + C64EA6CC169E847800778456 /* MCIMAPMultipart.h */, + C64EA6CD169E847800778456 /* MCIMAPNamespace.cc */, + C64EA6CE169E847800778456 /* MCIMAPNamespace.h */, + C64EA6CF169E847800778456 /* MCIMAPNamespaceItem.cc */, + C64EA6D0169E847800778456 /* MCIMAPNamespaceItem.h */, + C64EA6D1169E847800778456 /* MCIMAPPart.cc */, + C64EA6D2169E847800778456 /* MCIMAPPart.h */, + C64EA6D3169E847800778456 /* MCIMAPProgressCallback.h */, + C64EA6D4169E847800778456 /* MCIMAPSearchExpression.cc */, + C64EA6D5169E847800778456 /* MCIMAPSearchExpression.h */, + C64EA6D6169E847800778456 /* MCIMAPSession.cc */, + C64EA6D7169E847800778456 /* MCIMAPSession.h */, + ); + path = imap; + sourceTree = "<group>"; + }; + C64EA6D8169E847800778456 /* pop */ = { + isa = PBXGroup; + children = ( + C64EA6D9169E847800778456 /* MCPOP.h */, + C64EA6DA169E847800778456 /* MCPOPMessageInfo.cc */, + C64EA6DB169E847800778456 /* MCPOPMessageInfo.h */, + C64EA6DC169E847800778456 /* MCPOPProgressCallback.h */, + C64EA6DD169E847800778456 /* MCPOPSession.cc */, + C64EA6DE169E847800778456 /* MCPOPSession.h */, + ); + path = pop; + sourceTree = "<group>"; + }; + C64EA6DF169E847800778456 /* rfc822 */ = { + isa = PBXGroup; + children = ( + C64EA6E0169E847800778456 /* MCAttachment.cc */, + C64EA6E1169E847800778456 /* MCAttachment.h */, + C64EA6E2169E847800778456 /* MCMessageBuilder.cc */, + C64EA6E3169E847800778456 /* MCMessageBuilder.h */, + C64EA6E4169E847800778456 /* MCMessageParser.cc */, + C64EA6E5169E847800778456 /* MCMessageParser.h */, + C64EA6E6169E847800778456 /* MCMessagePart.cc */, + C64EA6E7169E847800778456 /* MCMessagePart.h */, + C64EA6E8169E847800778456 /* MCMultipart.cc */, + C64EA6E9169E847800778456 /* MCMultipart.h */, + C64EA6EA169E847800778456 /* MCRFC822.h */, + ); + path = rfc822; + sourceTree = "<group>"; + }; + C64EA6EB169E847800778456 /* smtp */ = { + isa = PBXGroup; + children = ( + C64EA6EC169E847800778456 /* MCSMTP.h */, + C64EA6ED169E847800778456 /* MCSMTPProgressCallback.h */, + C64EA6EE169E847800778456 /* MCSMTPSession.cc */, + C64EA6EF169E847800778456 /* MCSMTPSession.h */, + ); + path = smtp; + sourceTree = "<group>"; + }; + C64EA78E169F259200778456 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C64EA78F169F259200778456 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = "<group>"; + }; + C64EA7A916A00AF400778456 /* tests */ = { + isa = PBXGroup; + children = ( + C64EA7AA16A00AF600778456 /* main.mm */, + ); + name = tests; + path = ../tests; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C64EA536169E772200778456 /* mailcore2 */ = { + isa = PBXNativeTarget; + buildConfigurationList = C64EA53B169E772200778456 /* Build configuration list for PBXNativeTarget "mailcore2" */; + buildPhases = ( + C64EA780169E867600778456 /* ShellScript */, + C64EA74B169E854B00778456 /* CopyFiles */, + C64EA533169E772200778456 /* Sources */, + C64EA534169E772200778456 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mailcore2; + productName = mailcore; + productReference = C64EA537169E772200778456 /* libmailcore2.a */; + productType = "com.apple.product-type.library.static"; + }; + C64EA78B169F259200778456 /* tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = C64EA798169F259300778456 /* Build configuration list for PBXNativeTarget "tests" */; + buildPhases = ( + C64EA788169F259200778456 /* Sources */, + C64EA789169F259200778456 /* Frameworks */, + C64EA78A169F259200778456 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + C64EA7A3169F2A1D00778456 /* PBXTargetDependency */, + ); + name = tests; + productName = tests; + productReference = C64EA78C169F259200778456 /* tests */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + C64EA52E169E772200778456 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0450; + ORGANIZATIONNAME = MailCore; + }; + buildConfigurationList = C64EA531169E772200778456 /* Build configuration list for PBXProject "mailcore2" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = C64EA52C169E772200778456; + productRefGroup = C64EA538169E772200778456 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + C64EA536169E772200778456 /* mailcore2 */, + C64EA78B169F259200778456 /* tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + C64EA780169E867600778456 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = ""; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C64EA533169E772200778456 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C64EA6F4169E847800778456 /* MCAbstractMessage.cc in Sources */, + C64EA6F6169E847800778456 /* MCAbstractMessagePart.cc in Sources */, + C64EA6F8169E847800778456 /* MCAbstractMultipart.cc in Sources */, + C64EA6FA169E847800778456 /* MCAbstractPart.cc in Sources */, + C64EA6FC169E847800778456 /* MCAddress.cc in Sources */, + C64EA6FF169E847800778456 /* MCMessageHeader.cc in Sources */, + C64EA701169E847800778456 /* MCAutoreleasePool.cc in Sources */, + C64EA704169E847800778456 /* MCArray.cc in Sources */, + C64EA706169E847800778456 /* MCAssert.cc in Sources */, + C64EA708169E847800778456 /* MCData.cc in Sources */, + C64EA70A169E847800778456 /* MCHash.cc in Sources */, + C64EA70C169E847800778456 /* MCHashMap.cc in Sources */, + C64EA70E169E847800778456 /* MCLog.cc in Sources */, + C64EA710169E847800778456 /* MCObject.cc in Sources */, + C64EA712169E847800778456 /* MCRange.cc in Sources */, + C64EA714169E847800778456 /* MCSet.cc in Sources */, + C64EA716169E847800778456 /* MCString.cc in Sources */, + C64EA719169E847800778456 /* MCValue.cc in Sources */, + C64EA71C169E847800778456 /* MCMainThread.mm in Sources */, + C64EA71D169E847800778456 /* MCOperation.cc in Sources */, + C64EA720169E847800778456 /* MCOperationQueue.cc in Sources */, + C64EA723169E847800778456 /* MCIMAPFolder.cc in Sources */, + C64EA725169E847800778456 /* MCIMAPMessage.cc in Sources */, + C64EA727169E847800778456 /* MCIMAPMessagePart.cc in Sources */, + C64EA729169E847800778456 /* MCIMAPMultipart.cc in Sources */, + C64EA72B169E847800778456 /* MCIMAPNamespace.cc in Sources */, + C64EA72D169E847800778456 /* MCIMAPNamespaceItem.cc in Sources */, + C64EA72F169E847800778456 /* MCIMAPPart.cc in Sources */, + C64EA732169E847800778456 /* MCIMAPSearchExpression.cc in Sources */, + C64EA734169E847800778456 /* MCIMAPSession.cc in Sources */, + C64EA737169E847800778456 /* MCPOPMessageInfo.cc in Sources */, + C64EA73A169E847800778456 /* MCPOPSession.cc in Sources */, + C64EA73C169E847800778456 /* MCAttachment.cc in Sources */, + C64EA73E169E847800778456 /* MCMessageBuilder.cc in Sources */, + C64EA740169E847800778456 /* MCMessageParser.cc in Sources */, + C64EA742169E847800778456 /* MCMessagePart.cc in Sources */, + C64EA744169E847800778456 /* MCMultipart.cc in Sources */, + C64EA781169E89F600778456 /* MCSMTPSession.cc in Sources */, + C64EA784169F24E400778456 /* MCSMTPAsyncSession.cc in Sources */, + C64EA787169F252C00778456 /* MCSMTPSendWithRecipientOperation.cc in Sources */, + C64EA79E169F29A700778456 /* MCSMTPSendWithDataOperation.cc in Sources */, + C64EA7A1169F29D900778456 /* MCSMTPSendWithBuilderOperation.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C64EA788169F259200778456 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C64EA7AB16A00AF600778456 /* main.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C64EA7A3169F2A1D00778456 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C64EA536169E772200778456 /* mailcore2 */; + targetProxy = C64EA7A2169F2A1D00778456 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + C64EA539169E772200778456 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../Externals/libetpan/include", + "$(SRCROOT)/../Externals/icu4c/include", + /usr/include/libxml2, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/../Externals/libetpan/lib", + "$(SRCROOT)/../Externals/icu4c/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 10.8; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + C64EA53A169E772200778456 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_64_BIT)"; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(SRCROOT)/../Externals/libetpan/include", + "$(SRCROOT)/../Externals/icu4c/include", + /usr/include/libxml2, + ); + LIBRARY_SEARCH_PATHS = ( + "$(SRCROOT)/../Externals/libetpan/lib", + "$(SRCROOT)/../Externals/icu4c/lib", + ); + MACOSX_DEPLOYMENT_TARGET = 10.8; + SDKROOT = macosx; + }; + name = Release; + }; + C64EA53C169E772200778456 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../src/core/basetypes\"", + "\"$(SRCROOT)/../src/core/abstract\"", + ); + PRODUCT_NAME = mailcore2; + }; + name = Debug; + }; + C64EA53D169E772200778456 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + EXECUTABLE_PREFIX = lib; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../src/core/basetypes\"", + "\"$(SRCROOT)/../src/core/abstract\"", + ); + PRODUCT_NAME = mailcore2; + }; + name = Release; + }; + C64EA799169F259300778456 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = ( + "-letpan", + "-licudata", + "-licui18n", + "-licuuc", + "-lxml2", + "-lssl", + "-lcrypto", + "-lsasl2", + "-liconv", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + C64EA79A169F259300778456 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_LDFLAGS = ( + "-letpan", + "-licudata", + "-licui18n", + "-licuuc", + "-lxml2", + "-lssl", + "-lcrypto", + "-lsasl2", + "-liconv", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C64EA531169E772200778456 /* Build configuration list for PBXProject "mailcore2" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C64EA539169E772200778456 /* Debug */, + C64EA53A169E772200778456 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C64EA53B169E772200778456 /* Build configuration list for PBXNativeTarget "mailcore2" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C64EA53C169E772200778456 /* Debug */, + C64EA53D169E772200778456 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C64EA798169F259300778456 /* Build configuration list for PBXNativeTarget "tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C64EA799169F259300778456 /* Debug */, + C64EA79A169F259300778456 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = C64EA52E169E772200778456 /* Project object */; +} diff --git a/scripts/.DS_Store b/scripts/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/scripts/.DS_Store diff --git a/scripts/prepare-icu4c-macos.sh b/scripts/prepare-icu4c-macos.sh new file mode 100755 index 00000000..e9ab7d37 --- /dev/null +++ b/scripts/prepare-icu4c-macos.sh @@ -0,0 +1,123 @@ +#!/bin/sh + +versionfolder='50.1.1' +version='50_1_1' +url="http://download.icu-project.org/files/icu4c/$versionfolder/icu4c-$version-src.tgz" +package_filename="icu4c-$version-src.tgz" +arch="x86_64" +sysrootpath="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk" + +arch_flags="" +for current_arch in $arch ; do + arch_flags="$arch_flags -arch $current_arch" +done + +builddir="$HOME/MailCore-Builds/dependencies" +BUILD_TIMESTAMP=`date +'%Y%m%d%H%M%S'` +tempbuilddir="$builddir/workdir/$BUILD_TIMESTAMP" +mkdir -p "$tempbuilddir" +srcdir="$tempbuilddir/src" +logdir="$tempbuilddir/log" +resultdir="$builddir/builds" +tmpdir="$tempbuilddir/tmp" + +if test -f "$resultdir/icu4c-$version.zip" ; then + echo install from cache + rm -rf ../Externals/icu4c + mkdir -p ../Externals/tmp + unzip -q "$resultdir/icu4c-$version.zip" -d ../Externals/tmp + mv "../Externals/tmp/icu4c-$version/icu4c" ../Externals + rm -rf ../Externals/tmp + exit 0 +fi + +mkdir -p "$resultdir" +mkdir -p "$logdir" +mkdir -p "$tmpdir" +mkdir -p "$srcdir" + +pushd . >/dev/null + +echo get icu4c +cd "$srcdir" +if test -f "$builddir/downloads/$package_filename" ; then + cp "$builddir/downloads/$package_filename" . +else + curl -O "$url" + if test x$? != x0 ; then + echo fetch of icu4c failed + exit 1 + fi + mkdir -p "$builddir/downloads" + cp "$package_filename" "$builddir/downloads" +fi + +tar xf "$package_filename" + +echo building icu4c +cd "$srcdir/icu/source" +for cur_arch in $arch ; do + cur_arch_flags="-arch $cur_arch" + echo building icu4c for $cur_arch + export CFLAGS="$cur_arch_flags -isysroot $sysrootpath -mfix-and-continue -mmacosx-version-min=10.7" + export CXXFLAGS="$cur_arch_flags -isysroot $sysrootpath -mfix-and-continue -mmacosx-version-min=10.7" + export LDLAGS="$cur_arch_flags -isysroot $sysrootpath -mfix-and-continue -mmacosx-version-min=10.7" + mkdir -p "$tmpdir/bin/icu4c-$cur_arch" + cd "$tmpdir/bin/icu4c-$cur_arch" + if test "x$cur_arch" = xx86_64 ; then + "$srcdir/icu/source/configure" --enable-static --disable-shared --with-data-packaging=archive >> "$logdir/icu4c-build.log" + else + "$srcdir/icu/source/configure" --host=$cur_arch-apple-darwin10.6.0 --enable-static --disable-shared --with-data-packaging=archive --with-cross-build=$tmpdir/crossbuild/icu4c-x86_64 >> "$logdir/icu4c-build.log" + fi + make >> "$logdir/icu4c-build.log" + make install "prefix=$tmpdir/bin/icu4c-$cur_arch" >> "$logdir/icu4c-build.log" + if test "x$cur_arch" = xx86_64 ; then + make install "prefix=$tmpdir/crossbuild/icu4c-$cur_arch" >> "$logdir/icu4c-build.log" + fi + if test x$? != x0 ; then + echo build of icu4c failed + exit 1 + fi + #make distclean +done + +mkdir -p "$tmpdir/bin/icu4c" +cp -R "$tmpdir/bin/icu4c-x86_64/include" "$tmpdir/bin/icu4c" +mkdir -p "$tmpdir/bin/icu4c/share/icu" +cp "$tmpdir/bin/icu4c-x86_64/share/icu/$versionfolder/icudt50l.dat" "$tmpdir/bin/icu4c/share/icu" +mkdir -p "$tmpdir/bin/icu4c/lib" +cd "$tmpdir/bin" + +icui18n_paths="" +icudata_paths="" +icuuc_paths="" +for cur_arch in $arch ; do + icui18n_paths="$icui18n_paths icu4c-$cur_arch/lib/libicui18n.a" + icudata_paths="$icudata_paths icu4c-$cur_arch/lib/libicudata.a" + icuuc_paths="$icuuc_paths icu4c-$cur_arch/lib/libicuuc.a" +done +lipo -create $icui18n_paths -output icu4c/lib/libicui18n.a +lipo -create $icudata_paths -output icu4c/lib/libicudata.a +lipo -create $icuuc_paths -output icu4c/lib/libicuuc.a + +cd "$tmpdir/bin" +mkdir -p "icu4c-$version" +mv icu4c "icu4c-$version" +zip -qry "$resultdir/icu4c-$version.zip" "icu4c-$version" +rm -f "$resultdir/icu4c-latest.zip" +cd "$resultdir" +ln -s "icu4c-$version.zip" "icu4c-latest.zip" + +echo build of icu4c-$version done + +popd + +rm -rf ../Externals/icu4c +mkdir -p ../Externals/tmp +unzip -q "$resultdir/icu4c-$version.zip" -d ../Externals/tmp +mv "../Externals/tmp/icu4c-$version/icu4c" ../Externals +rm -rf ../Externals/tmp + +echo cleaning +rm -rf "$tempbuilddir" +echo "$tempbuilddir" diff --git a/scripts/prepare-libetpan-macos.sh b/scripts/prepare-libetpan-macos.sh new file mode 100755 index 00000000..c6079c34 --- /dev/null +++ b/scripts/prepare-libetpan-macos.sh @@ -0,0 +1,83 @@ +#!/bin/sh + +arch="x86_64" +sysrootpath="/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.8.sdk" +url="https://github.com/dinhviethoa/libetpan.git" + +arch_flags="" +for current_arch in $arch ; do + arch_flags="$arch_flags -arch $current_arch" +done + +builddir="$HOME/MailCore-Builds/dependencies" +BUILD_TIMESTAMP=`date +'%Y%m%d%H%M%S'` +tempbuilddir="$builddir/workdir/$BUILD_TIMESTAMP" +mkdir -p "$tempbuilddir" +srcdir="$tempbuilddir/src" +logdir="$tempbuilddir/log" +resultdir="$builddir/builds" +tmpdir="$tempbuilddir/tmp" + +mkdir -p "$resultdir" +mkdir -p "$logdir" +mkdir -p "$tmpdir" +mkdir -p "$srcdir" + +pushd . >/dev/null +cd "$builddir/downloads" +if test -d libetpan ; then + cd libetpan + git pull --rebase +else + git clone $url + cd libetpan +fi +version=`git rev-parse HEAD | cut -c1-10` + +if test -f "$resultdir/libetpan-$version.zip" ; then + echo install from cache + popd >/dev/null + rm -rf ../Externals/libetpan + mkdir -p ../Externals/tmp + unzip -q "$resultdir/libetpan-$version.zip" -d ../Externals/tmp + mv "../Externals/tmp/libetpan-$version/libetpan" ../Externals + rm -rf ../Externals/tmp + exit 0 +fi +popd >/dev/null + +pushd . >/dev/null + +cp -R "$builddir/downloads/libetpan" "$srcdir/libetpan" +echo building libetpan +cd "$srcdir/libetpan" +./autogen.sh +cd "$srcdir/libetpan/build-mac" +./update.sh + +xcodebuild -target "static libetpan" -configuration Release SYMROOT="$tmpdir/bin" OBJROOT="$tmpdir/obj" +echo finished + +cd "$tmpdir/bin" +mkdir -p "libetpan-$version/libetpan" +mkdir -p "libetpan-$version/libetpan/lib" +mv Release/include "libetpan-$version/libetpan" +mv Release/libetpan.a "libetpan-$version/libetpan/lib" +zip -qry "$resultdir/libetpan-$version.zip" "libetpan-$version" +rm -f "$resultdir/libetpan-latest.zip" +cd "$resultdir" +ln -s "libetpan-$version.zip" "libetpan-latest.zip" + +echo build of libetpan-$version done + +popd >/dev/null + +rm -rf ../Externals/libetpan +mkdir -p ../Externals/tmp +unzip -q "$resultdir/libetpan-$version.zip" -d ../Externals/tmp +mv "../Externals/tmp/libetpan-$version/libetpan" ../Externals +rm -rf ../Externals/tmp + +echo cleaning +rm -rf "$tempbuilddir" +echo "$tempbuilddir" diff --git a/scripts/prepare.sh b/scripts/prepare.sh new file mode 100755 index 00000000..3e3cb9b1 --- /dev/null +++ b/scripts/prepare.sh @@ -0,0 +1,3 @@ +#!/bin/sh +./prepare-icu4c-macos.sh +./prepare-libetpan-macos.sh diff --git a/src/async/imap/IMAPAsyncSession.h b/src/async/imap/IMAPAsyncSession.h new file mode 100644 index 00000000..1e32bc46 --- /dev/null +++ b/src/async/imap/IMAPAsyncSession.h @@ -0,0 +1,122 @@ +#ifndef __MAILCORE_MCIMAPASYNCSESSION_H + +#define __MAILCORE_MCIMAPASYNCSESSION_H + +#include <mailcore/CBaseTypes.h> +#include <mailcore/MessageConstants.h> + +namespace mailcore { + + class IMAPOperation; + class IMAPFetchFoldersOperation; + class IMAPAppendOperation; + class IMAPCopyOperation; + class IMAPFetchMessagesOperation; + class IMAPIdleOperation; + class IMAPSession; + class IMAPNamespace; + + class IMAPSession : public Object { + private: + String * mHostname; + unsigned int mPort; + String * mUsername; + String * mPassword; + AuthType mAuthType; + ConnectionType mConnectionType; + bool mCheckCertificateEnabled; + bool mVoIPEnabled; + char mDelimiter; + IMAPNamespace * mDefaultNamespace; + + IMAPSession * mSession; + + public: + IMAPAsyncSession(); + virtual ~IMAPAsyncSession(); + + virtual String * className(); + + virtual void setHostname(String * hostname); + virtual String * hostname(); + + virtual void setPort(unsigned int port); + virtual unsigned int port(); + + virtual void setUsername(String * login); + virtual String * username(); + + virtual void setPassword(String * password); + virtual String * password(); + + virtual void setAuthType(AuthType authType); + virtual AuthType authType(); + + virtual void setConnectionType(ConnectionType connectionType); + virtual ConnectionType connectionType(); + + virtual void setTimeout(time_t timeout); + virtual time_t timeout(); + + virtual void setCheckCertificateEnabled(bool enabled); + virtual bool isCheckCertificateEnabled(); + + virtual void setVoIPEnabled(bool enabled); + virtual bool isVoIPEnabled(); + + virtual void setDelimiter(char delimiter); + virtual char delimiter(); + + virtual void setDefaultNamespace(IMAPNamespace * ns); + virtual IMAPNamespace * defaultNamespace(); + + virtual IMAPOperation * select(String * folder); + + virtual IMAPFetchFoldersOperation * fetchSubscribedFolders(); + virtual IMAPFetchFoldersOperation * fetchAllFolders(); + + virtual IMAPOperation * renameFolder(String * folder, String * otherName); + virtual IMAPOperation * deleteFolder(String * folder, ErrorCode * pError); + virtual IMAPOperation * createFolder(String * folder, ErrorCode * pError); + + virtual IMAPOperation * subscribeFolder(String * folder, ErrorCode * pError); + virtual IMAPOperation * unsubscribeFolder(String * folder, ErrorCode * pError); + + virtual IMAPAppendOperation * appendMessage(String * folder, Data * messageData, MessageFlag flags); + + virtual IMAPCopyOperation * copyMessages(String * folder, Array * uidSet, String * destFolder); + + virtual IMAPOperation * expunge(String * folder); + + virtual IMAPFetchMessagesOperation * fetchMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + uint32_t firstUID, uint32_t lastUID); + virtual IMAPFetchMessagesOperation * fetchMessagesByNumber(String * folder, IMAPMessagesRequestKind requestKind, + uint32_t firstNumber, uint32_t lastNumber); + virtual IMAPFetchMessagesOperation * fetchMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + Array * numbers); + virtual IMAPFetchMessagesOperation * fetchMessagesByNumber(String * folder, IMAPMessagesRequestKind requestKind, + Array * numbers); + virtual IMAPFetchMessageOperation * fetchMessageByUID(String * folder, uint32_t uid); + virtual IMAPFetchMessageOperation * fetchMessageAttachmentByUID(String * folder, uint32_t uid, String * partID, + Encoding encoding, unsigned int expectedSize); + + virtual IMAPOperation * storeFlags(String * folder, Array * uids, IMAPStoreFlagsRequestKind kind, MessageFlag flags); + virtual IMAPOperation * storeLabels(String * folder, Array * uids, IMAPStoreFlagsRequestKind kind, Array * labels); + + virtual Array * search(String * folder, IMAPSearchKind kind, String * searchString); + virtual Array * search(String * folder, IMAPSearchExpression * expression); + + virtual IMAPIdleOperation * idle(String * folder, uint32_t lastKnownUID); + + virtual IMAPOperation * connect(); + virtual IMAPOperation * disconnect(); + + virtual IMAPOperation * fetchNamespace(); + + virtual IMAPOperation * login(); + + virtual IMAPOperation * identity(String * vendor, String * name, String * version); + }; +} + +#endif diff --git a/src/async/smtp/MCSMTPAsyncSession.cc b/src/async/smtp/MCSMTPAsyncSession.cc new file mode 100644 index 00000000..9798fc0c --- /dev/null +++ b/src/async/smtp/MCSMTPAsyncSession.cc @@ -0,0 +1,147 @@ +#include "MCSMTPAsyncSession.h" + +#include "MCSMTPSession.h" +#include "MCSMTPSendWithRecipientOperation.h" +#include "MCSMTPSendWithDataOperation.h" +#include "MCSMTPSendWithBuilderOperation.h" + +using namespace mailcore; + +SMTPAsyncSession::SMTPAsyncSession() +{ + mSession = new SMTPSession(); + mQueue = new OperationQueue(); +} + +SMTPAsyncSession::~SMTPAsyncSession() +{ + mQueue->release(); + mSession->release(); +} + +String * SMTPAsyncSession::className() +{ + return MCSTR("SMTPAsyncSession"); +} + +void SMTPAsyncSession::setHostname(String * hostname) +{ + mSession->setHostname(hostname); +} + +String * SMTPAsyncSession::hostname() +{ + return mSession->hostname(); +} + +void SMTPAsyncSession::setPort(unsigned int port) +{ + mSession->setPort(port); +} + +unsigned int SMTPAsyncSession::port() +{ + return mSession->port(); +} + +void SMTPAsyncSession::setUsername(String * username) +{ + return mSession->setUsername(username); +} + +String * SMTPAsyncSession::username() +{ + return mSession->username(); +} + +void SMTPAsyncSession::setPassword(String * password) +{ + mSession->setPassword(password); +} + +String * SMTPAsyncSession::password() +{ + return mSession->password(); +} + +void SMTPAsyncSession::setAuthType(AuthType authType) +{ + mSession->setAuthType(authType); +} + +AuthType SMTPAsyncSession::authType() +{ + return mSession->authType(); +} + +void SMTPAsyncSession::setConnectionType(ConnectionType connectionType) +{ + mSession->setConnectionType(connectionType); +} + +ConnectionType SMTPAsyncSession::connectionType() +{ + return mSession->connectionType(); +} + +void SMTPAsyncSession::setTimeout(time_t timeout) +{ + return mSession->setTimeout(timeout); +} + +time_t SMTPAsyncSession::timeout() +{ + return mSession->timeout(); +} + +void SMTPAsyncSession::setCheckCertificateEnabled(bool enabled) +{ + return mSession->setCheckCertificateEnabled(enabled); +} + +bool SMTPAsyncSession::isCheckCertificateEnabled() +{ + return mSession->isCheckCertificateEnabled(); +} + +void SMTPAsyncSession::setUseHeloIPEnabled(bool enabled) +{ + mSession->setUseHeloIPEnabled(enabled); +} + +bool SMTPAsyncSession::useHeloIPEnabled() +{ + return mSession->useHeloIPEnabled(); +} + +SMTPOperation * sendMessage(Address * from, Array * recipients, Data * messageData) +{ +#if 0 + SMTPSendWithRecipientOperation * op = new SMTPSendWithRecipientOperation(); + op->setFrom(from); + op->setRecipients(recipients); + op->messageData(messageData); + return (SMTPOperation *) op->autorelease(); +#endif + return NULL; +} + +SMTPOperation * sendMessage(Data * messageData) +{ +#if 0 + SMTPSendWithDataOperation * op = new SMTPSendWithDataOperation(); + op->setData(messageData); + return (SMTPOperation *) op->autorelease(); +#endif + return NULL; +} + +SMTPOperation * sendMessage(MessageBuilder * msg) +{ +#if 0 + SMTPSendWithBuilderOperation * op = new SMTPSendWithBuilderOperation(); + op->setBuilder(msg); + return (SMTPOperation *) op->autorelease(); +#endif + return NULL; +} diff --git a/src/async/smtp/MCSMTPAsyncSession.h b/src/async/smtp/MCSMTPAsyncSession.h new file mode 100644 index 00000000..8e0e50b0 --- /dev/null +++ b/src/async/smtp/MCSMTPAsyncSession.h @@ -0,0 +1,73 @@ +#ifndef __MAILCORE_MCSMTPASYNCSESSION_H + +#define __MAILCORE_MCSMTPASYNCSESSION_H + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCMessageConstants.h> +#include <libetpan/libetpan.h> + +namespace mailcore { + + class MessageBuilder; + class SMTPOperation; + class SMTPSession; + class Address; + + class SMTPAsyncSession : public Object { + private: + String * mHostname; + unsigned int mPort; + String * mUsername; + String * mPassword; + AuthType mAuthType; + ConnectionType mConnectionType; + time_t mTimeout; + bool mCheckCertificateEnabled; + bool mUseHeloIPEnabled; + + SMTPSession * mSession; + OperationQueue * mQueue; + + void queue(SMTPOperation * op); + + public: + SMTPAsyncSession(); + virtual ~SMTPAsyncSession(); + + virtual String * className(); + + virtual void setHostname(String * hostname); + virtual String * hostname(); + + virtual void setPort(unsigned int port); + virtual unsigned int port(); + + virtual void setUsername(String * username); + virtual String * username(); + + virtual void setPassword(String * password); + virtual String * password(); + + virtual void setAuthType(AuthType authType); + virtual AuthType authType(); + + virtual void setConnectionType(ConnectionType connectionType); + virtual ConnectionType connectionType(); + + virtual void setTimeout(time_t timeout); + virtual time_t timeout(); + + virtual void setCheckCertificateEnabled(bool enabled); + virtual bool isCheckCertificateEnabled(); + + virtual void setUseHeloIPEnabled(bool enabled); + virtual bool useHeloIPEnabled(); + + virtual SMTPOperation * sendMessage(Address * from, Array * recipients, Data * messageData); + virtual SMTPOperation * sendMessage(Data * messageData); + virtual SMTPOperation * sendMessage(MessageBuilder * msg); + }; + +} + +#endif diff --git a/src/async/smtp/MCSMTPSendWithBuilderOperation.cc b/src/async/smtp/MCSMTPSendWithBuilderOperation.cc new file mode 100644 index 00000000..560998ff --- /dev/null +++ b/src/async/smtp/MCSMTPSendWithBuilderOperation.cc @@ -0,0 +1,9 @@ +// +// SMTPSendWithBuilderOperation.cc +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#include "MCSMTPSendWithBuilderOperation.h" diff --git a/src/async/smtp/MCSMTPSendWithBuilderOperation.h b/src/async/smtp/MCSMTPSendWithBuilderOperation.h new file mode 100644 index 00000000..d306e30c --- /dev/null +++ b/src/async/smtp/MCSMTPSendWithBuilderOperation.h @@ -0,0 +1,14 @@ +// +// SMTPSendWithBuilderOperation.h +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#ifndef __mailcore2__MCSMTPSendWithBuilderOperation__ +#define __mailcore2__MCSMTPSendWithBuilderOperation__ + +#include <iostream> + +#endif /* defined(__mailcore2__SMTPSendWithBuilderOperation__) */ diff --git a/src/async/smtp/MCSMTPSendWithDataOperation.cc b/src/async/smtp/MCSMTPSendWithDataOperation.cc new file mode 100644 index 00000000..ab42aa0e --- /dev/null +++ b/src/async/smtp/MCSMTPSendWithDataOperation.cc @@ -0,0 +1,9 @@ +// +// SMTPSendWithDataOperation.cc +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#include "MCSMTPSendWithDataOperation.h" diff --git a/src/async/smtp/MCSMTPSendWithDataOperation.h b/src/async/smtp/MCSMTPSendWithDataOperation.h new file mode 100644 index 00000000..c13ee07d --- /dev/null +++ b/src/async/smtp/MCSMTPSendWithDataOperation.h @@ -0,0 +1,14 @@ +// +// SMTPSendWithDataOperation.h +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#ifndef __mailcore2__MCSMTPSendWithDataOperation__ +#define __mailcore2__MCSMTPSendWithDataOperation__ + +#include <iostream> + +#endif /* defined(__mailcore2__SMTPSendWithDataOperation__) */ diff --git a/src/async/smtp/MCSMTPSendWithRecipientOperation.cc b/src/async/smtp/MCSMTPSendWithRecipientOperation.cc new file mode 100644 index 00000000..5fee7fa3 --- /dev/null +++ b/src/async/smtp/MCSMTPSendWithRecipientOperation.cc @@ -0,0 +1,9 @@ +// +// SMTPSendWithRecipientOperation.cc +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#include "MCSMTPSendWithRecipientOperation.h" diff --git a/src/async/smtp/MCSMTPSendWithRecipientOperation.h b/src/async/smtp/MCSMTPSendWithRecipientOperation.h new file mode 100644 index 00000000..f4134688 --- /dev/null +++ b/src/async/smtp/MCSMTPSendWithRecipientOperation.h @@ -0,0 +1,13 @@ +// +// SMTPSendWithRecipientOperation.h +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#ifndef __mailcore2__MCSMTPSendWithRecipientOperation__ +#define __mailcore2__MCSMTPSendWithRecipientOperation__ + + +#endif /* defined(__mailcore2__SMTPSendWithRecipientOperation__) */ diff --git a/src/core/MCCore.h b/src/core/MCCore.h new file mode 100644 index 00000000..9c1e0d42 --- /dev/null +++ b/src/core/MCCore.h @@ -0,0 +1,19 @@ +// +// Core.h +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#ifndef __MAILCORE_MCCORE_H_ +#define __MAILCORE_MCCORE_H_ + +#include <mailcore/MCAbstract.h> +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCIMAP.h> +#include <mailcore/MCPOP.h> +#include <mailcore/MCRFC822.h> +#include <mailcore/MCSMTP.h> + +#endif diff --git a/src/core/abstract/.DS_Store b/src/core/abstract/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/src/core/abstract/.DS_Store diff --git a/src/core/abstract/MCAbstract.h b/src/core/abstract/MCAbstract.h new file mode 100644 index 00000000..fd36af46 --- /dev/null +++ b/src/core/abstract/MCAbstract.h @@ -0,0 +1,13 @@ +#ifndef __MAILCORE_MCABSTRACT_H + +#define __MAILCORE_MCABSTRACT_H + +#include <mailcore/MCAbstractMessage.h> +#include <mailcore/MCAbstractMessagePart.h> +#include <mailcore/MCAbstractMultipart.h> +#include <mailcore/MCAbstractPart.h> +#include <mailcore/MCAddress.h> +#include <mailcore/MCMessageConstants.h> +#include <mailcore/MCMessageHeader.h> + +#endif diff --git a/src/core/abstract/MCAbstractMessage.cc b/src/core/abstract/MCAbstractMessage.cc new file mode 100644 index 00000000..00a4b9b9 --- /dev/null +++ b/src/core/abstract/MCAbstractMessage.cc @@ -0,0 +1,65 @@ +#include "MCAbstractMessage.h" + +#include "MCMessageHeader.h" + +using namespace mailcore; + +AbstractMessage::AbstractMessage() +{ + init(); +} + +AbstractMessage::AbstractMessage(AbstractMessage * other) +{ + init(); + mHeader = (MessageHeader *) MC_SAFE_COPY(other->mHeader); +} + +void AbstractMessage::init() +{ + mHeader = NULL; +} + +AbstractMessage::~AbstractMessage() +{ + MC_SAFE_RELEASE(mHeader); +} + +String * AbstractMessage::description() +{ + if (mHeader != NULL) { + String * result = String::string(); + result->appendUTF8Format("<%s:%p\n", className()->UTF8Characters(), this); + result->appendString(mHeader->description()); + result->appendUTF8Characters(">"); + return result; + } + else { + return Object::description(); + } +} + +#if 0 +String * AbstractMessage::className() +{ + return MCSTR("MessageHeader"); +} +#endif + +Object * AbstractMessage::copy() +{ + return new AbstractMessage(this); +} + +MessageHeader * AbstractMessage::header() +{ + if (mHeader == NULL) { + mHeader = new MessageHeader(); + } + return mHeader; +} + +void AbstractMessage::setHeader(MessageHeader * header) +{ + MC_SAFE_REPLACE_RETAIN(MessageHeader, mHeader, header); +} diff --git a/src/core/abstract/MCAbstractMessage.h b/src/core/abstract/MCAbstractMessage.h new file mode 100644 index 00000000..e5b62c8a --- /dev/null +++ b/src/core/abstract/MCAbstractMessage.h @@ -0,0 +1,29 @@ +#ifndef __MAILCORE_MCABSTRACTMESSAGE_H_ +#define __MAILCORE_MCABSTRACTMESSAGE_H_ + +#include <mailcore/MCBaseTypes.h> + +namespace mailcore { + + class MessageHeader; + + class AbstractMessage : public Object { + private: + MessageHeader * mHeader; + void init(); + + public: + AbstractMessage(); + AbstractMessage(AbstractMessage * other); + virtual ~AbstractMessage(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual MessageHeader * header(); + virtual void setHeader(MessageHeader * header); + }; +} + +#endif diff --git a/src/core/abstract/MCAbstractMessagePart.cc b/src/core/abstract/MCAbstractMessagePart.cc new file mode 100644 index 00000000..6939d060 --- /dev/null +++ b/src/core/abstract/MCAbstractMessagePart.cc @@ -0,0 +1,88 @@ +#include "MCAbstractMessagePart.h" + +#include "MCMessageHeader.h" + +using namespace mailcore; + +void AbstractMessagePart::init() +{ + mMainPart = NULL; + mHeader = NULL; +} + +AbstractMessagePart::AbstractMessagePart() +{ +} + +AbstractMessagePart::AbstractMessagePart(AbstractMessagePart * other) +{ + if (other->mainPart() != NULL) { + setMainPart((AbstractPart *) other->mainPart()->copy()->autorelease()); + } + if (other->mHeader != NULL) { + setHeader((MessageHeader *) other->header()->copy()->autorelease()); + } +} + +AbstractMessagePart::~AbstractMessagePart() +{ + MC_SAFE_RELEASE(mMainPart); + MC_SAFE_RELEASE(mHeader); +} + +String * AbstractMessagePart::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p %s>", className(), this, mMainPart->description()); + return result; +} + +#if 0 +String * AbstractMessagePart::className() +{ + return MCSTR("AbstractMessagePart"); +} +#endif + +Object * AbstractMessagePart::copy() +{ + return new AbstractMessagePart(this); +} + +MessageHeader * AbstractMessagePart::header() +{ + if (mHeader == NULL) { + mHeader = new MessageHeader(); + } + return mHeader; +} + +void AbstractMessagePart::setHeader(MessageHeader * header) +{ + MC_SAFE_REPLACE_RETAIN(MessageHeader, mHeader, header); +} + +AbstractPart * AbstractMessagePart::mainPart() +{ + return mMainPart; +} + +void AbstractMessagePart::setMainPart(AbstractPart * mainPart) +{ + MC_SAFE_REPLACE_RETAIN(AbstractPart, mMainPart, mainPart); + applyMessage(); +} + +void AbstractMessagePart::applyMessage() +{ + if (mMainPart == NULL) + return; + + mMainPart->setMessage(message()); +} + +void AbstractMessagePart::setMessage(AbstractMessage * message) +{ + AbstractPart::setMessage(message); + applyMessage(); +} diff --git a/src/core/abstract/MCAbstractMessagePart.h b/src/core/abstract/MCAbstractMessagePart.h new file mode 100644 index 00000000..a855202c --- /dev/null +++ b/src/core/abstract/MCAbstractMessagePart.h @@ -0,0 +1,39 @@ +#ifndef __MAILCORE_MCABSTRACTMESSAGEPART_H_ + +#define __MAILCORE_MCABSTRACTMESSAGEPART_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractPart.h> + +namespace mailcore { + + class MessageHeader; + + class AbstractMessagePart : public AbstractPart { + private: + AbstractPart * mMainPart; + MessageHeader * mHeader; + void init(); + void applyMessage(); + + public: + AbstractMessagePart(); + AbstractMessagePart(AbstractMessagePart * other); + virtual ~AbstractMessagePart(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual MessageHeader * header(); + virtual void setHeader(MessageHeader * header); + + virtual AbstractPart * mainPart(); + virtual void setMainPart(AbstractPart * mainPart); + + virtual void setMessage(AbstractMessage * message); + }; +} + + +#endif diff --git a/src/core/abstract/MCAbstractMultipart.cc b/src/core/abstract/MCAbstractMultipart.cc new file mode 100644 index 00000000..fa48d959 --- /dev/null +++ b/src/core/abstract/MCAbstractMultipart.cc @@ -0,0 +1,95 @@ +#include "MCAbstractMultipart.h" + +using namespace mailcore; + +AbstractMultipart::AbstractMultipart() +{ + init(); +} + +AbstractMultipart::AbstractMultipart(AbstractMultipart * other) : AbstractPart(other) +{ + init(); + + setPartType(other->partType()); + Array * parts = Array::array(); + for(unsigned int i = 0 ; i < other->parts()->count() ; i ++) { + AbstractPart * part = (AbstractPart *) other->parts()->objectAtIndex(i); + parts->addObject(part->copy()->autorelease()); + } + setParts(parts); +} + +void AbstractMultipart::init() +{ + mParts = NULL; + setPartType(PartTypeMultipartMixed); +} + +AbstractMultipart::~AbstractMultipart() +{ + MC_SAFE_RELEASE(mParts); +} + +Array * AbstractMultipart::parts() +{ + return mParts; +} + +void AbstractMultipart::setParts(Array * parts) +{ + MC_SAFE_REPLACE_COPY(Array, mParts, parts); + applyMessage(); +} + +String * AbstractMultipart::description() +{ + String * result = String::string(); + + const char * partTypeName = NULL; + switch (partType()) { + default: + case PartTypeMultipartMixed: + partTypeName = "mixed"; + break; + case PartTypeMultipartRelated: + partTypeName = "related"; + break; + case PartTypeMultipartAlternative: + partTypeName = "alernative"; + break; + } + + result->appendUTF8Format("<%s:%p %s %s>", + MCUTF8(className()), this, partTypeName, MCUTF8(mParts->description())); + return result; +} + +#if 0 +String * AbstractMultipart::className() +{ + return MCSTR("AbstractMultipart"); +} +#endif + +Object * AbstractMultipart::copy() +{ + return new AbstractMultipart(this); +} + +void AbstractMultipart::applyMessage() +{ + if (mParts == NULL) + return; + + for(unsigned int i = 0 ; i < mParts->count() ; i ++) { + AbstractPart * part = (AbstractPart *) mParts->objectAtIndex(i); + part->setMessage(message()); + } +} + +void AbstractMultipart::setMessage(AbstractMessage * message) +{ + AbstractPart::setMessage(message); + applyMessage(); +} diff --git a/src/core/abstract/MCAbstractMultipart.h b/src/core/abstract/MCAbstractMultipart.h new file mode 100644 index 00000000..c731ed7b --- /dev/null +++ b/src/core/abstract/MCAbstractMultipart.h @@ -0,0 +1,32 @@ +#ifndef __MAILCORE_MCABSTRACTMULTIPART_H + +#define __MAILCORE_MCABSTRACTMULTIPART_H + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractPart.h> + +namespace mailcore { + + class AbstractMultipart : public AbstractPart { + private: + Array * mParts; + void init(); + void applyMessage(); + + public: + AbstractMultipart(); + AbstractMultipart(AbstractMultipart * other); + virtual ~AbstractMultipart(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual Array * parts(); + virtual void setParts(Array * parts); + + virtual void setMessage(AbstractMessage * message); + }; +} + +#endif diff --git a/src/core/abstract/MCAbstractPart.cc b/src/core/abstract/MCAbstractPart.cc new file mode 100644 index 00000000..30c4c8f0 --- /dev/null +++ b/src/core/abstract/MCAbstractPart.cc @@ -0,0 +1,223 @@ +#include "MCAbstractPart.h" + +#include <string.h> +#include <stdlib.h> + +using namespace mailcore; + +AbstractPart::AbstractPart() +{ + init(); +} + +AbstractPart::AbstractPart(AbstractPart * other) +{ + init(); + setFilename(other->mFilename); + setMimeType(other->mMimeType); + setCharset(other->mCharset); + setContentID(other->mContentID); + setContentLocation(other->mContentLocation); + setInlineAttachment(other->mInlineAttachment); + setPartType(other->mPartType); +} + +void AbstractPart::init() +{ + mFilename = NULL; + mMimeType = NULL; + mCharset = NULL; + mContentID = NULL; + mContentLocation = NULL; + mInlineAttachment = false; + mPartType = PartTypeSingle; +} + +AbstractPart::~AbstractPart() +{ + MC_SAFE_RELEASE(mFilename); + MC_SAFE_RELEASE(mMimeType); + MC_SAFE_RELEASE(mCharset); + MC_SAFE_RELEASE(mContentID); + MC_SAFE_RELEASE(mContentLocation); +} + +String * AbstractPart::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p\n", className()->UTF8Characters(), this); + if (mFilename != NULL) { + result->appendUTF8Format("filename: %s\n", mFilename->UTF8Characters()); + } + if (mMimeType != NULL) { + result->appendUTF8Format("mime type: %s\n", mMimeType->UTF8Characters()); + } + if (mCharset != NULL) { + result->appendUTF8Format("charset: %s\n", mCharset->UTF8Characters()); + } + if (mContentID != NULL) { + result->appendUTF8Format("content-ID: %s\n", mContentID->UTF8Characters()); + } + if (mContentLocation != NULL) { + result->appendUTF8Format("content-location: %s\n", mContentLocation->UTF8Characters()); + } + result->appendUTF8Format("inline: %i\n", mInlineAttachment); + result->appendUTF8Format(">"); + + return result; +} + +#if 0 +String * AbstractPart::className() +{ + return MCSTR("AbstractPart"); +} +#endif + +Object * AbstractPart::copy() +{ + return new AbstractPart(this); +} + +PartType AbstractPart::partType() +{ + return mPartType; +} + +void AbstractPart::setPartType(PartType type) +{ + mPartType = type; +} + +String * AbstractPart::filename() +{ + return mFilename; +} + +void AbstractPart::setFilename(String * filename) +{ + MC_SAFE_REPLACE_COPY(String, mFilename, filename); +} + +String * AbstractPart::mimeType() +{ + return mMimeType; +} + +void AbstractPart::setMimeType(String * mimeType) +{ + MC_SAFE_REPLACE_COPY(String, mMimeType, mimeType); +} + +String * AbstractPart::charset() +{ + return mCharset; +} + +void AbstractPart::setCharset(String * charset) +{ + MC_SAFE_REPLACE_COPY(String, mCharset, charset); +} + +String * AbstractPart::contentID() +{ + return mContentID; +} + +void AbstractPart::setContentID(String * contentID) +{ + MC_SAFE_REPLACE_COPY(String, mContentID, contentID); +} + +String * AbstractPart::contentLocation() +{ + return mContentLocation; +} + +void AbstractPart::setContentLocation(String * contentLocation) +{ + MC_SAFE_REPLACE_COPY(String, mContentLocation, contentLocation); +} + +bool AbstractPart::isInlineAttachment() +{ + return mInlineAttachment; +} + +void AbstractPart::setInlineAttachment(bool inlineAttachment) +{ + mInlineAttachment = inlineAttachment; +} + +AbstractMessage * AbstractPart::message() +{ + return mMessage; +} + +void AbstractPart::setMessage(AbstractMessage * message) +{ + mMessage = message; +} + +void AbstractPart::importIMAPFields(struct mailimap_body_fields * fields, + struct mailimap_body_ext_1part * extension) +{ + if (fields->bd_parameter != NULL) { + clistiter * cur; + + for(cur = clist_begin(fields->bd_parameter->pa_list) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimap_single_body_fld_param * imap_param; + + imap_param = (struct mailimap_single_body_fld_param *) clist_content(cur); + + if (strcasecmp(imap_param->pa_name, "name") == 0) { + setFilename(String::stringByDecodingMIMEHeaderValue(imap_param->pa_value)); + } + else if (strcasecmp(imap_param->pa_name, "charset") == 0) { + setCharset(String::stringByDecodingMIMEHeaderValue(imap_param->pa_value)); + } + } + } + if (fields->bd_id != NULL) { + char * contentid; + size_t cur_token; + int r; + + cur_token = 0; + r = mailimf_msg_id_parse(fields->bd_id, strlen(fields->bd_id), + &cur_token, &contentid); + if (r == MAILIMF_NO_ERROR) { + // msg id + setContentID(String::stringWithUTF8Characters(contentid)); + free(contentid); + } + } + + if (extension != NULL) { + if (extension->bd_disposition != NULL) { + if (strcasecmp(extension->bd_disposition->dsp_type, "inline") == 0) { + setInlineAttachment(true); + } + + if (extension->bd_disposition->dsp_attributes != NULL) { + clistiter * cur; + + for(cur = clist_begin(extension->bd_disposition->dsp_attributes->pa_list) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimap_single_body_fld_param * imap_param; + + imap_param = (struct mailimap_single_body_fld_param *) clist_content(cur); + + if (strcasecmp(imap_param->pa_name, "filename") == 0) { + setFilename(String::stringByDecodingMIMEHeaderValue(imap_param->pa_value)); + } + } + } + } + + if (extension->bd_loc != NULL) { + setContentLocation(String::stringWithUTF8Characters(extension->bd_loc)); + } + } +} diff --git a/src/core/abstract/MCAbstractPart.h b/src/core/abstract/MCAbstractPart.h new file mode 100644 index 00000000..356ed256 --- /dev/null +++ b/src/core/abstract/MCAbstractPart.h @@ -0,0 +1,63 @@ +#ifndef __MAILCORE_MCABSTRACTPART_H_ + +#define __MAILCORE_MCABSTRACTPART_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCMessageConstants.h> + +namespace mailcore { + + class AbstractMessage; + + class AbstractPart : public Object { + private: + String * mFilename; + String * mMimeType; + String * mCharset; + String * mContentID; + String * mContentLocation; + bool mInlineAttachment; + PartType mPartType; + AbstractMessage * mMessage; // weak + void init(); + + public: + AbstractPart(); + AbstractPart(AbstractPart * other); + virtual ~AbstractPart(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual PartType partType(); + virtual void setPartType(PartType type); + + virtual String * filename(); + virtual void setFilename(String * filename); + + virtual String * mimeType(); + virtual void setMimeType(String * mimeType); + + virtual String * charset(); + virtual void setCharset(String * charset); + + virtual String * contentID(); + virtual void setContentID(String * contentID); + + virtual String * contentLocation(); + virtual void setContentLocation(String * contentLocation); + + virtual bool isInlineAttachment(); + virtual void setInlineAttachment(bool inlineAttachment); + + virtual AbstractMessage * message(); + virtual void setMessage(AbstractMessage * message); + + virtual void importIMAPFields(struct mailimap_body_fields * fields, + struct mailimap_body_ext_1part * extension); + }; + +} + +#endif diff --git a/src/core/abstract/MCAddress.cc b/src/core/abstract/MCAddress.cc new file mode 100644 index 00000000..25bed974 --- /dev/null +++ b/src/core/abstract/MCAddress.cc @@ -0,0 +1,364 @@ +#include "MCAddress.h" + +#include <string.h> + +using namespace mailcore; + +Address::Address() +{ + init(); +} + +Address::Address(Address * other) +{ + init(); + setDisplayName(other->displayName()); + setMailbox(other->mailbox()); +} + +void Address::init() +{ + mDisplayName = NULL; + mMailbox = NULL; +} + +Address::~Address() +{ + MC_SAFE_RELEASE(mDisplayName); + MC_SAFE_RELEASE(mMailbox); +} + +Address * Address::addressWithDisplayName(String * displayName, String * mailbox) +{ + Address * result = new Address(); + result->setDisplayName(displayName); + result->setMailbox(mailbox); + return (Address *) result->autorelease(); +} + +Address * Address::addressWithMailbox(String * mailbox) +{ + return addressWithDisplayName(NULL, mailbox); +} + +Address * Address::addressWithIMFMailbox(struct mailimf_mailbox * mailbox) +{ + Address * address; + + address = new Address(); + if (mailbox->mb_display_name != NULL) { + address->setDisplayName(String::stringByDecodingMIMEHeaderValue(mailbox->mb_display_name)); + } + if (mailbox->mb_addr_spec != NULL) { + address->setMailbox(String::stringWithUTF8Characters(mailbox->mb_addr_spec)); + } + if (address->mailbox() == NULL) { + address->setMailbox(String::string()); + } + + return (Address *) address->autorelease(); +} + +Address * Address::addressWithIMAPAddress(struct mailimap_address * imap_addr) +{ + char * dsp_name; + Address * address; + String * mailbox; + + if (imap_addr->ad_personal_name == NULL) + dsp_name = NULL; + else { + dsp_name = imap_addr->ad_personal_name; + } + + if (imap_addr->ad_host_name == NULL) { + const char * addr; + + if (imap_addr->ad_mailbox_name == NULL) { + addr = ""; + } + else { + addr = imap_addr->ad_mailbox_name; + } + mailbox = String::stringWithUTF8Characters(addr); + if (mailbox == NULL) { + mailbox = MCSTR(""); + } + } + else if (imap_addr->ad_mailbox_name == NULL) { + // fix by Gabor Cselle, (http://gaborcselle.com/), reported 8/16/2009 + mailbox = String::stringWithUTF8Format("@%s", imap_addr->ad_host_name); + } + else { + mailbox = String::stringWithUTF8Format("%s@%s", imap_addr->ad_mailbox_name, imap_addr->ad_host_name); + } + + address = new Address(); + if (dsp_name != NULL) { + address->setDisplayName(String::stringByDecodingMIMEHeaderValue(dsp_name)); + } + address->setMailbox(mailbox); + + return (Address *) address->autorelease(); +} + +Address * Address::addressWithRFC822String(String * RFC822String) +{ + const char * utf8String; + size_t currentIndex; + struct mailimf_mailbox * mb; + int r; + Address * result; + + utf8String = RFC822String->UTF8Characters(); + currentIndex = 0; + r = mailimf_mailbox_parse(utf8String, strlen(utf8String), ¤tIndex, &mb); + if (r != MAILIMF_NO_ERROR) + return NULL; + + result = addressWithIMFMailbox(mb); + mailimf_mailbox_free(mb); + + return result; +} + +Address * Address::addressWithNonEncodedIMFMailbox(struct mailimf_mailbox * mailbox) +{ + Address * address; + + address = new Address(); + if (mailbox->mb_display_name != NULL) { + address->setDisplayName(String::stringWithUTF8Characters(mailbox->mb_display_name)); + } + if (mailbox->mb_addr_spec != NULL) { + address->setMailbox(String::stringWithUTF8Characters(mailbox->mb_addr_spec)); + } + if (address->mailbox() == NULL) { + address->setMailbox(String::string()); + } + + return (Address *) address->autorelease(); +} + +Address * Address::addressWithNonEncodedRFC822String(String * nonEncodedRFC822String) +{ + const char * utf8String; + size_t currentIndex; + struct mailimf_mailbox * mb; + int r; + Address * result; + + utf8String = nonEncodedRFC822String->UTF8Characters(); + currentIndex = 0; + r = mailimf_mailbox_parse(utf8String, strlen(utf8String), ¤tIndex, &mb); + if (r != MAILIMF_NO_ERROR) + return NULL; + + result = addressWithNonEncodedIMFMailbox(mb); + mailimf_mailbox_free(mb); + + return result; +} + +String * Address::description() +{ + String * result = String::string(); + result->appendString(className()); + result->appendUTF8Format(":%p ", this); + if (mDisplayName != NULL) { + result->appendString(mDisplayName); + } + result->appendUTF8Characters(" <"); + if (mMailbox != NULL) { + result->appendString(mMailbox); + } + result->appendUTF8Characters(">"); + + return result; +} + +#if 0 +String * Address::className() +{ + return MCSTR("Address"); +} +#endif + +bool Address::isEqual(Object * otherObject) +{ + Address * otherAddress = (Address *) otherObject; + + if (mDisplayName == NULL) { + if (otherAddress->displayName() != NULL) { + return false; + } + } + else if (mDisplayName != NULL) { + if (otherAddress->displayName() == NULL) { + return false; + } + else { + if (!mDisplayName->isEqual(otherAddress->displayName())) + return false; + } + } + + if (mMailbox == NULL) { + if (otherAddress->mailbox() != NULL) { + return false; + } + } + else if (mMailbox != NULL) { + if (otherAddress->mailbox() == NULL) { + return false; + } + else { + if (!mMailbox->isEqual(otherAddress->mailbox())) + return false; + } + } + + return true; +} + +unsigned int Address::hash() +{ + unsigned int value; + + value = 0; + if (mDisplayName != NULL) { + value += mDisplayName->hash(); + } + if (mMailbox != NULL) { + value += mMailbox->hash(); + } + + return value; +} + +Object * Address::copy() +{ + return new Address(this); +} + +void Address::setDisplayName(String * displayName) +{ + MC_SAFE_REPLACE_COPY(String, mDisplayName, displayName); +} + +String * Address::displayName() +{ + return mDisplayName; +} + +void Address::setMailbox(String * mailbox) +{ + MC_SAFE_REPLACE_COPY(String, mMailbox, mailbox); +} + +String * Address::mailbox() +{ + return mMailbox; +} + +struct mailimf_address * Address::createIMFAddress() +{ + struct mailimf_mailbox * mailbox; + struct mailimf_address * result; + + mailbox = createIMFMailbox(); + result = mailimf_address_new(MAILIMF_ADDRESS_MAILBOX, mailbox, NULL); + + return result; +} + +struct mailimf_mailbox * Address::createIMFMailbox() +{ + struct mailimf_mailbox * result; + char * display_name; + char * addr_spec; + + display_name = NULL; + if (displayName() != NULL) { + if (displayName()->length() > 0) { + Data * data; + + data = displayName()->encodedAddressDisplayNameValue(); + if (data->bytes() != NULL) { + display_name = strdup(data->bytes()); + } + } + } + addr_spec = strdup(mailbox()->UTF8Characters()); + result = mailimf_mailbox_new(display_name, addr_spec); + + return result; +} + +String * Address::RFC822String() +{ + struct mailimf_mailbox * mb; + MMAPString * str; + int col; + struct mailimf_mailbox_list * mb_list; + clist * list; + String * result; + + mb = createIMFMailbox(); + + list = clist_new(); + clist_append(list, mb); + mb_list = mailimf_mailbox_list_new(list); + + str = mmap_string_new(""); + col = 0; + mailimf_mailbox_list_write_mem(str, &col, mb_list); + + result = String::stringWithUTF8Characters(str->str); + + mailimf_mailbox_list_free(mb_list); + mmap_string_free(str); + + return result; +} + +String * Address::nonEncodedRFC822String() +{ + struct mailimf_mailbox * mb; + MMAPString * str; + int col; + struct mailimf_mailbox_list * mb_list; + clist * list; + String * result; + char * display_name; + char * addr_spec; + + display_name = NULL; + if (displayName() != NULL) { + if (displayName()->length() > 0) { + display_name = strdup(displayName()->UTF8Characters()); + } + } + if ((mailbox() == NULL) || (mailbox()->length() == 0)) { + addr_spec = strdup("invalid"); + } + else { + addr_spec = strdup(mailbox()->UTF8Characters()); + } + mb = mailimf_mailbox_new(display_name, addr_spec); + + list = clist_new(); + clist_append(list, mb); + mb_list = mailimf_mailbox_list_new(list); + + str = mmap_string_new(""); + col = 0; + mailimf_mailbox_list_write_mem(str, &col, mb_list); + + result = String::stringWithUTF8Characters(str->str); + + mailimf_mailbox_list_free(mb_list); + mmap_string_free(str); + + return result; +} diff --git a/src/core/abstract/MCAddress.h b/src/core/abstract/MCAddress.h new file mode 100644 index 00000000..227c1136 --- /dev/null +++ b/src/core/abstract/MCAddress.h @@ -0,0 +1,55 @@ +#ifndef __MAILCORE_MCADDRESS_H_ + +#define __MAILCORE_MCADDRESS_H_ + +#include <libetpan/libetpan.h> +#include <mailcore/MCBaseTypes.h> + +namespace mailcore { + + class Address : public Object { + private: + String * mDisplayName; + String * mMailbox; + void init(); + + public: + Address(); + Address(Address * other); + virtual ~Address(); + + static Address * addressWithDisplayName(String * displayName, String * mailbox); + static Address * addressWithMailbox(String * mailbox); + static Address * addressWithRFC822String(String * RFC822String); + static Address * addressWithNonEncodedRFC822String(String * nonEncodedRFC822String); + + virtual String * description(); + //virtual String * className(); + + virtual bool isEqual(Object * otherObject); + virtual unsigned int hash(); + + virtual Object * copy(); + + virtual void setDisplayName(String * displayName); + virtual String * displayName(); + + virtual void setMailbox(String * address); + virtual String * mailbox(); + + virtual String * RFC822String(); + virtual String * nonEncodedRFC822String(); + + // Additions + static Address * addressWithIMFMailbox(struct mailimf_mailbox * mb); + static Address * addressWithNonEncodedIMFMailbox(struct mailimf_mailbox * mb); + static Address * addressWithIMAPAddress(struct mailimap_address * imap_addr); + + // Must be released + virtual struct mailimf_address * createIMFAddress(); + virtual struct mailimf_mailbox * createIMFMailbox(); + }; + +} + +#endif diff --git a/src/core/abstract/MCMessageConstants.h b/src/core/abstract/MCMessageConstants.h new file mode 100644 index 00000000..ab30b8c4 --- /dev/null +++ b/src/core/abstract/MCMessageConstants.h @@ -0,0 +1,208 @@ +#ifndef __MAILCORE_MCMESSAGECONSTANTS_H_ +#define __MAILCORE_MCMESSAGECONSTANTS_H_ + +namespace mailcore { + + enum ConnectionType { + ConnectionTypeClear = 1 << 0, + ConnectionTypeStartTLS = 1 << 1, + ConnectionTypeTLS = 1 << 2, + }; + + enum AuthType { + AuthTypeSASLNone = 0, + AuthTypeSASLCRAMMD5 = 1 << 0, + AuthTypeSASLPlain = 1 << 1, + AuthTypeSASLGSSAPI = 1 << 2, + AuthTypeSASLDIGESTMD5 = 1 << 3, + AuthTypeSASLLogin = 1 << 4, + AuthTypeSASLSRP = 1 << 5, + AuthTypeSASLNTLM = 1 << 6, + AuthTypeSASLKerberosV4 = 1 << 7, + }; + + enum IMAPFolderFlag { + IMAPFolderFlagNone = 0, + IMAPFolderFlagMarked = 1 << 0, + IMAPFolderFlagUnmarked = 1 << 1, + IMAPFolderFlagNoSelect = 1 << 2, + IMAPFolderFlagNoInferiors = 1 << 3, + IMAPFolderFlagInbox = 1 << 4, + IMAPFolderFlagSentMail = 1 << 5, + IMAPFolderFlagStarred = 1 << 6, + IMAPFolderFlagAllMail = 1 << 7, + IMAPFolderFlagTrash = 1 << 8, + IMAPFolderFlagDrafts = 1 << 9, + IMAPFolderFlagSpam = 1 << 10, + IMAPFolderFlagImportant = 1 << 11, + IMAPFolderFlagArchive = 1 << 12, + }; + + enum MessageFlag { + MessageFlagNone = 0, + MessageFlagSeen = 1 << 0, + MessageFlagAnswered = 1 << 1, + MessageFlagFlagged = 1 << 2, + MessageFlagDeleted = 1 << 3, + MessageFlagDraft = 1 << 4, + MessageFlagMDNSent = 1 << 5, + MessageFlagForwarded = 1 << 6, + MessageFlagSubmitPending = 1 << 7, + MessageFlagSubmitted = 1 << 8, + } ; + + enum IMAPMessagesRequestKind { + IMAPMessagesRequestKindUid = 0, // This is the default and it's always fetched + IMAPMessagesRequestKindFlags = 1 << 0, + IMAPMessagesRequestKindHeaders = 1 << 1, + IMAPMessagesRequestKindStructure = 1 << 2, + IMAPMessagesRequestKindInternalDate = 1 << 3, + IMAPMessagesRequestKindFullHeaders = 1 << 4, + IMAPMessagesRequestKindHeaderSubject = 1 << 5, + IMAPMessagesRequestKindGmailLabels = 1 << 6, + }; + + enum IMAPFetchRequestType { + IMAPFetchRequestTypeUID = 0, + IMAPFetchRequestTypeSequence = 1 + }; + + enum IMAPStoreFlagsRequestKind { + IMAPStoreFlagsRequestKindAdd, + IMAPStoreFlagsRequestKindRemove, + IMAPStoreFlagsRequestKindSet, + }; + + enum IMAPWorkaround { + IMAPWorkaroundGmail = 1 << 0, + IMAPWorkaroundYahoo = 1 << 1, + IMAPWorkaroundExchange2003 = 1 << 2, + }; + + enum IMAPCapability { + IMAPCapabilityACL, + IMAPCapabilityBinary, + IMAPCapabilityCatenate, + IMAPCapabilityChildren, + IMAPCapabilityCompressDeflate, + IMAPCapabilityCondstore, + IMAPCapabilityEnable, + IMAPCapabilityIdle, + IMAPCapabilityLiteralPlus, + IMAPCapabilityMultiAppend, + IMAPCapabilityNamespace, + IMAPCapabilityQResync, + IMAPCapabilityQuota, + IMAPCapabilitySort, + IMAPCapabilityStartTLS, + IMAPCapabilityThreadOrderedSubject, + IMAPCapabilityThreadReferences, + IMAPCapabilityUIDPlus, + IMAPCapabilityUnselect, + IMAPCapabilityXList, + IMAPCapabilityAuthAnonymous, + IMAPCapabilityAuthCRAMMD5, + MAPCapabilityAuthDigestMD5, + IMAPCapabilityAuthExternal, + IMAPCapabilityAuthGSSAPI, + IMAPCapabilityAuthKerberosV4, + IMAPCapabilityAuthLogin, + IMAPCapabilityAuthNTLM, + IMAPCapabilityAuthOTP, + IMAPCapabilityAuthPlain, + IMAPCapabilityAuthSKey, + IMAPCapabilityAuthSRP, + }; + + enum POPCapability { + POPCapabilityNone, + POPCapabilityStartTLS, + POPCapabilityTop, + POPCapabilityUser, + POPCapabilityRespCodes, + POPCapabilityPipelining, + POPCapabilityUIDL, + POPCapabilitySASL, + POPCapabilityAuthAnonymous, + POPCapabilityAuthCRAMMD5, + POPCapabilityAuthDigestMD5, + POPCapabilityAuthExternal, + POPCapabilityAuthGSSAPI, + POPCapabilityAuthKerberosV4, + POPCapabilityAuthLogin, + POPCapabilityAuthNTLM, + POPCapabilityAuthOTP, + POPCapabilityAuthPlain, + POPCapabilityAuthSKey, + POPCapabilityAuthSRP, + }; + + enum Encoding { + Encoding7Bit = 0, // should match MAILIMAP_BODY_FLD_ENC_7BIT + Encoding8Bit = 1, // should match MAILIMAP_BODY_FLD_ENC_8BIT + EncodingBinary = 2, // should match MAILIMAP_BODY_FLD_ENC_BINARY + EncodingBase64 = 3, // should match MAILIMAP_BODY_FLD_ENC_BASE64 + EncodingQuotedPrintable = 4, // should match MAILIMAP_BODY_FLD_ENC_QUOTED_PRINTABLE + EncodingOther = 5, // should match MAILIMAP_BODY_FLD_ENC_OTHER + // negative values should be used for other encoding + EncodingUUEncode = -1 + }; + + enum IMAPSearchKind { + IMAPSearchKindNone, + IMAPSearchKindFrom, + IMAPSearchKindRecipient, + IMAPSearchKindSubject, + IMAPSearchKindContent, + IMAPSearchKindHeader, + IMAPSearchKindOr, + IMAPSearchKindAnd, + }; + + enum ErrorCode { + ErrorNone, + ErrorConnection, + ErrorTLSNotAvailable, + ErrorTLSCertificate, + ErrorParse, + ErrorCertificate, + ErrorAuthentication, + ErrorGmailIMAPNotEnabled, + ErrorGmailExceededBandwidthLimit, + ErrorGmailTooManySimultaneousConnections, + ErrorMobileMeMoved, + ErrorYahooUnavailable, + ErrorNonExistantFolder, + ErrorRename, + ErrorDelete, + ErrorCreate, + ErrorSubscribe, + ErrorAppend, + ErrorCopy, + ErrorExpunge, + ErrorFetch, + ErrorIdle, + ErrorIdentity, + ErrorNamespace, + ErrorStore, + ErrorStartTLSNotAvailable, + ErrorSendMessageIllegalAttachment, + ErrorStorageLimit, + ErrorSendMessageNotAllowed, + ErrorNeedsConnectToWebmail, + ErrorSendMessage, + ErrorAuthenticationRequired, + ErrorFetchMessageList, + ErrorDeleteMessage, + }; + + enum PartType { + PartTypeSingle, + PartTypeMessage, + PartTypeMultipartMixed, + PartTypeMultipartRelated, + PartTypeMultipartAlternative, + }; +} + +#endif
\ No newline at end of file diff --git a/src/core/abstract/MCMessageHeader.cc b/src/core/abstract/MCMessageHeader.cc new file mode 100644 index 00000000..06ad386b --- /dev/null +++ b/src/core/abstract/MCMessageHeader.cc @@ -0,0 +1,1054 @@ +#include "MCMessageHeader.h" + +#include "MCAddress.h" + +#include <string.h> +#include <unistd.h> + +using namespace mailcore; + +static time_t timestamp_from_date(struct mailimf_date_time * date_time); +static struct mailimf_date_time * get_date_from_timestamp(time_t timeval); +static time_t timestamp_from_imap_date(struct mailimap_date_time * date_time); +static time_t mkgmtime(struct tm * tmp); +static int tmcomp(struct tm * atmp, struct tm * btmp); + +static struct mailimf_address_list * lep_address_list_from_array(Array * addresses); +static struct mailimf_mailbox_list * lep_mailbox_list_from_array(Array * addresses); +static Array * lep_address_list_from_lep_addr(struct mailimf_address_list * addr_list); +static Array * lep_address_list_from_lep_mailbox(struct mailimf_mailbox_list * mb_list); + +static Array * msg_id_to_string_array(clist * msgids); +static clist * msg_id_from_string_array(Array * msgids); + +#define MAX_HOSTNAME 512 + +MessageHeader::MessageHeader() +{ + init(true, true); +} + +MessageHeader::MessageHeader(MessageHeader * other) +{ + init(false, other->mMessageID == NULL); + setMessageID(other->mMessageID); + setReferences(other->mReferences); + setInReplyTo(other->mInReplyTo); + setSender(other->mSender); + setFrom(other->mFrom); + setTo(other->mTo); + setCc(other->mCc); + setBcc(other->mBcc); + setReplyTo(other->mReplyTo); + setSubject(other->mSubject); + setUserAgent(other->mUserAgent); +} + +void MessageHeader::init(bool generateDate, bool generateMessageID) +{ + mMessageID = NULL; + mReferences = NULL; + mInReplyTo = NULL; + mSender = NULL; + mFrom = NULL; + mTo = NULL; + mCc = NULL; + mBcc = NULL; + mReplyTo = NULL; + mSubject = NULL; + mDate = (time_t) -1; + mReceivedDate = (time_t) -1; + mUserAgent = NULL; + + if (generateDate) { + time_t date; + date = time(NULL); + setDate(date); + setReceivedDate(date); + } + if (generateMessageID) { + static String * hostname = NULL; + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + pthread_mutex_lock(&lock); + if (hostname == NULL) { + char name[MAX_HOSTNAME]; + int r; + + r = gethostname(name, MAX_HOSTNAME); + if (r < 0) { + hostname = NULL; + } + else { + hostname = new String(name); + } + if (hostname == NULL) { + hostname = new String("localhost"); + } + } + pthread_mutex_unlock(&lock); + + String * messageID = new String(); + messageID->appendString(String::uuidString()); + messageID->appendUTF8Characters("@"); + messageID->appendString(hostname); + setMessageID(messageID); + messageID->release(); + } +} + +MessageHeader::~MessageHeader() +{ + MC_SAFE_RELEASE(mMessageID); + MC_SAFE_RELEASE(mReferences); + MC_SAFE_RELEASE(mInReplyTo); + MC_SAFE_RELEASE(mSender); + MC_SAFE_RELEASE(mFrom); + MC_SAFE_RELEASE(mTo); + MC_SAFE_RELEASE(mCc); + MC_SAFE_RELEASE(mBcc); + MC_SAFE_RELEASE(mReplyTo); + MC_SAFE_RELEASE(mSubject); + MC_SAFE_RELEASE(mUserAgent); +} + +String * MessageHeader::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p\n", className()->UTF8Characters(), this); + if (mMessageID != NULL) { + result->appendUTF8Format("Message-ID: %s\n", mMessageID->UTF8Characters()); + } + if (mReferences != NULL) { + result->appendUTF8Format("References: %s\n", mReferences->description()->UTF8Characters()); + } + if (mInReplyTo != NULL) { + result->appendUTF8Format("In-Reply-To: %s\n", mInReplyTo->description()->UTF8Characters()); + } + if (mSender != NULL) { + result->appendUTF8Format("Sender: %s\n", mSender->description()->UTF8Characters()); + } + if (mFrom != NULL) { + result->appendUTF8Format("From: %s\n", mFrom->description()->UTF8Characters()); + } + if (mTo != NULL) { + result->appendUTF8Format("To: %s\n", mTo->description()->UTF8Characters()); + } + if (mCc != NULL) { + result->appendUTF8Format("Cc: %s\n", mCc->description()->UTF8Characters()); + } + if (mBcc != NULL) { + result->appendUTF8Format("Bcc: %s\n", mBcc->description()->UTF8Characters()); + } + if (mReplyTo != NULL) { + result->appendUTF8Format("Reply-To: %s\n", mReplyTo->description()->UTF8Characters()); + } + if (mSubject != NULL) { + result->appendUTF8Format("Subject: %s\n", mSubject->UTF8Characters()); + } + if (mUserAgent != NULL) { + result->appendUTF8Format("X-Mailer: %s\n", mUserAgent->UTF8Characters()); + } + result->appendUTF8Format(">"); + + return result; +} + +#if 0 +String * MessageHeader::className() +{ + return MCSTR("MessageHeader"); +} +#endif + +Object * MessageHeader::copy() +{ + return new MessageHeader(this); +} + +void MessageHeader::setMessageID(String * messageID) +{ + MC_SAFE_REPLACE_COPY(String, mMessageID, messageID); +} + +String * MessageHeader::messageID() +{ + return mMessageID; +} + +void MessageHeader::setReferences(Array * references) +{ + MC_SAFE_REPLACE_COPY(Array, mReferences, references); +} + +Array * MessageHeader::references() +{ + return mReferences; +} + +void MessageHeader::setInReplyTo(Array * inReplyTo) +{ + MC_SAFE_REPLACE_COPY(Array, mInReplyTo, inReplyTo); +} + +Array * MessageHeader::inReplyTo() +{ + return mInReplyTo; +} + +void MessageHeader::setDate(time_t date) +{ + mDate = date; +} + +time_t MessageHeader::date() +{ + return mDate; +} + +void MessageHeader::setReceivedDate(time_t date) +{ + mReceivedDate = date; +} + +time_t MessageHeader::receivedDate() +{ + return mReceivedDate; +} + +void MessageHeader::setSender(Address * sender) +{ + MC_SAFE_REPLACE_RETAIN(Address, mSender, sender); +} + +Address * MessageHeader::sender() +{ + return mSender; +} + +void MessageHeader::setFrom(Address * from) +{ + MC_SAFE_REPLACE_RETAIN(Address, mFrom, from); +} + +Address * MessageHeader::from() +{ + return mFrom; +} + +void MessageHeader::setTo(Array * to) +{ + MC_SAFE_REPLACE_COPY(Array, mTo, to); +} + +Array * MessageHeader::to() +{ + return mTo; +} + +void MessageHeader::setCc(Array * cc) +{ + MC_SAFE_REPLACE_COPY(Array, mCc, cc); +} + +Array * MessageHeader::cc() +{ + return mCc; +} + +void MessageHeader::setBcc(Array * bcc) +{ + MC_SAFE_REPLACE_COPY(Array, mBcc, bcc); +} + +Array * MessageHeader::bcc() +{ + return mBcc; +} + +void MessageHeader::setReplyTo(Array * replyTo) +{ + MC_SAFE_REPLACE_COPY(Array, mReplyTo, replyTo); +} + +Array * MessageHeader::replyTo() +{ + return mReplyTo; +} + +void MessageHeader::setSubject(String * subject) +{ + MC_SAFE_REPLACE_COPY(String, mSubject, subject); +} + +String * MessageHeader::subject() +{ + return mSubject; +} + +void MessageHeader::setUserAgent(String * userAgent) +{ + MC_SAFE_REPLACE_COPY(String, mUserAgent, userAgent); +} + +String * MessageHeader::userAgent() +{ + return mUserAgent; +} + +String * MessageHeader::extractedSubject() +{ + return subject()->extractedSubject(); +} + +String * MessageHeader::partialExtractedSubject() +{ + return subject()->extractedSubjectAndKeepBracket(true); +} + +void MessageHeader::importHeadersData(Data * data) +{ + size_t cur_token; + struct mailimf_fields * fields; + int r; + + cur_token = 0; + r = mailimf_fields_parse(data->bytes(), data->length(), &cur_token, &fields); + if (r != MAILIMF_NO_ERROR) { + return; + } + + importIMFFields(fields); + + mailimf_fields_free(fields); +} + +void MessageHeader::importIMFFields(struct mailimf_fields * fields) +{ + struct mailimf_single_fields single_fields; + + mailimf_single_fields_init(&single_fields, fields); + + /* date */ + + if (single_fields.fld_orig_date != NULL) { + time_t timestamp; + timestamp = timestamp_from_date(single_fields.fld_orig_date->dt_date_time); + setDate(timestamp); + setReceivedDate(timestamp); + //MCLog("%lu %lu", (unsigned long) timestamp, date()); + } + + /* subject */ + if (single_fields.fld_subject != NULL) { + char * subject; + + subject = single_fields.fld_subject->sbj_value; + setSubject(String::stringByDecodingMIMEHeaderValue(subject)); + } + + /* sender */ + if (single_fields.fld_sender != NULL) { + struct mailimf_mailbox * mb; + Address * address; + + mb = single_fields.fld_sender->snd_mb; + if (mb != NULL) { + address = Address::addressWithIMFMailbox(mb); + setSender(address); + } + } + + /* from */ + if (single_fields.fld_from != NULL) { + struct mailimf_mailbox_list * mb_list; + Array * addresses; + + mb_list = single_fields.fld_from->frm_mb_list; + addresses = lep_address_list_from_lep_mailbox(mb_list); + if (addresses->count() > 0) { + setFrom((Address *) (addresses->objectAtIndex(0))); + } + } + + /* replyto */ + if (single_fields.fld_reply_to != NULL) { + struct mailimf_address_list * addr_list; + Array * addresses; + + addr_list = single_fields.fld_reply_to->rt_addr_list; + addresses = lep_address_list_from_lep_addr(addr_list); + setReplyTo(addresses); + } + + /* to */ + if (single_fields.fld_to != NULL) { + struct mailimf_address_list * addr_list; + Array * addresses; + + addr_list = single_fields.fld_to->to_addr_list; + addresses = lep_address_list_from_lep_addr(addr_list); + setTo(addresses); + } + + /* cc */ + if (single_fields.fld_cc != NULL) { + struct mailimf_address_list * addr_list; + Array * addresses; + + addr_list = single_fields.fld_cc->cc_addr_list; + addresses = lep_address_list_from_lep_addr(addr_list); + setCc(addresses); + } + + /* bcc */ + if (single_fields.fld_bcc != NULL) { + struct mailimf_address_list * addr_list; + Array * addresses; + + addr_list = single_fields.fld_bcc->bcc_addr_list; + addresses = lep_address_list_from_lep_addr(addr_list); + setBcc(addresses); + } + + /* msgid */ + if (single_fields.fld_message_id != NULL) { + char * msgid; + String * str; + + msgid = single_fields.fld_message_id->mid_value; + str = String::stringWithUTF8Characters(msgid); + setMessageID(str); + } + + /* references */ + if (single_fields.fld_references != NULL) { + clist * msg_id_list; + Array * msgids; + + msg_id_list = single_fields.fld_references->mid_list; + msgids = msg_id_to_string_array(msg_id_list); + setReferences(msgids); + } + + /* inreplyto */ + if (single_fields.fld_in_reply_to != NULL) { + clist * msg_id_list; + Array * msgids; + + msg_id_list = single_fields.fld_in_reply_to->mid_list; + msgids = msg_id_to_string_array(msg_id_list); + setInReplyTo(msgids); + } +} + +static time_t timestamp_from_date(struct mailimf_date_time * date_time) +{ + struct tm tmval; + time_t timeval; + int zone_min; + int zone_hour; + + tmval.tm_sec = date_time->dt_sec; + tmval.tm_min = date_time->dt_min; + tmval.tm_hour = date_time->dt_hour; + tmval.tm_mday = date_time->dt_day; + tmval.tm_mon = date_time->dt_month - 1; + if (date_time->dt_year < 1000) { + // workaround when century is not given in year + tmval.tm_year = date_time->dt_year + 2000 - 1900; + } + else { + tmval.tm_year = date_time->dt_year - 1900; + } + + timeval = mkgmtime(&tmval); + + if (date_time->dt_zone >= 0) { + zone_hour = date_time->dt_zone / 100; + zone_min = date_time->dt_zone % 100; + } + else { + zone_hour = -((- date_time->dt_zone) / 100); + zone_min = -((- date_time->dt_zone) % 100); + } + timeval -= zone_hour * 3600 + zone_min * 60; + + return timeval; +} + +static struct mailimf_date_time * get_date_from_timestamp(time_t timeval) +{ + struct tm gmt; + struct tm lt; + int off; + struct mailimf_date_time * date_time; + int sign; + int hour; + int min; + + gmtime_r(&timeval, &gmt); + localtime_r(&timeval, <); + + off = (mkgmtime(<) - mkgmtime(&gmt)) / 60; + if (off < 0) { + sign = -1; + } + else { + sign = 1; + } + off = off * sign; + min = off % 60; + hour = off / 60; + off = hour * 100 + min; + off = off * sign; + + date_time = mailimf_date_time_new(lt.tm_mday, lt.tm_mon + 1, + lt.tm_year + 1900, + lt.tm_hour, lt.tm_min, lt.tm_sec, + off); + + return date_time; +} + +static time_t timestamp_from_imap_date(struct mailimap_date_time * date_time) +{ + struct tm tmval; + time_t timeval; + int zone_min; + int zone_hour; + + tmval.tm_sec = date_time->dt_sec; + tmval.tm_min = date_time->dt_min; + tmval.tm_hour = date_time->dt_hour; + tmval.tm_mday = date_time->dt_day; + tmval.tm_mon = date_time->dt_month - 1; + if (date_time->dt_year < 1000) { + // workaround when century is not given in year + tmval.tm_year = date_time->dt_year + 2000 - 1900; + } + else { + tmval.tm_year = date_time->dt_year - 1900; + } + + timeval = mkgmtime(&tmval); + + if (date_time->dt_zone >= 0) { + zone_hour = date_time->dt_zone / 100; + zone_min = date_time->dt_zone % 100; + } + else { + zone_hour = -((- date_time->dt_zone) / 100); + zone_min = -((- date_time->dt_zone) % 100); + } + timeval -= zone_hour * 3600 + zone_min * 60; + + return timeval; +} + +#define INVALID_TIMESTAMP (-1) + +static int tmcomp(struct tm * atmp, struct tm * btmp) +{ + register int result; + + if ((result = (atmp->tm_year - btmp->tm_year)) == 0 && + (result = (atmp->tm_mon - btmp->tm_mon)) == 0 && + (result = (atmp->tm_mday - btmp->tm_mday)) == 0 && + (result = (atmp->tm_hour - btmp->tm_hour)) == 0 && + (result = (atmp->tm_min - btmp->tm_min)) == 0) + result = atmp->tm_sec - btmp->tm_sec; + return result; +} + +static time_t mkgmtime(struct tm * tmp) +{ + register int dir; + register int bits; + register int saved_seconds; + time_t t; + struct tm yourtm, mytm; + + yourtm = *tmp; + saved_seconds = yourtm.tm_sec; + yourtm.tm_sec = 0; + /* + ** Calculate the number of magnitude bits in a time_t + ** (this works regardless of whether time_t is + ** signed or unsigned, though lint complains if unsigned). + */ + for (bits = 0, t = 1; t > 0; ++bits, t <<= 1) + ; + /* + ** If time_t is signed, then 0 is the median value, + ** if time_t is unsigned, then 1 << bits is median. + */ + if(bits > 40) bits = 40; + t = (t < 0) ? 0 : ((time_t) 1 << bits); + for ( ; ; ) { + gmtime_r(&t, &mytm); + dir = tmcomp(&mytm, &yourtm); + if (dir != 0) { + if (bits-- < 0) { + return INVALID_TIMESTAMP; + } + if (bits < 0) + --t; + else if (dir > 0) + t -= (time_t) 1 << bits; + else t += (time_t) 1 << bits; + continue; + } + break; + } + t += saved_seconds; + return t; +} + +#pragma mark RFC 2822 mailbox conversion + +static Array * lep_address_list_from_lep_mailbox(struct mailimf_mailbox_list * mb_list) +{ + Array * result; + clistiter * cur; + + result = Array::array(); + for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailimf_mailbox * mb; + Address * address; + + mb = (struct mailimf_mailbox *) clist_content(cur); + address = Address::addressWithIMFMailbox(mb); + result->addObject(address); + } + + return result; +} + +static Array * lep_address_list_from_lep_addr(struct mailimf_address_list * addr_list) +{ + Array * result; + clistiter * cur; + + result = Array::array(); + + if (addr_list == NULL) { + return result; + } + + if (addr_list->ad_list == NULL) { + return result; + } + + for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimf_address * addr; + + addr = (struct mailimf_address *) clist_content(cur); + switch (addr->ad_type) { + case MAILIMF_ADDRESS_MAILBOX: + { + Address * address; + + address = Address::addressWithIMFMailbox(addr->ad_data.ad_mailbox); + result->addObject(address); + break; + } + + case MAILIMF_ADDRESS_GROUP: + { + if (addr->ad_data.ad_group->grp_mb_list != NULL) { + Array * subArray; + + subArray = lep_address_list_from_lep_mailbox(addr->ad_data.ad_group->grp_mb_list); + result->addObjectsFromArray(subArray); + } + break; + } + } + } + + return result; +} + +static struct mailimf_mailbox_list * lep_mailbox_list_from_array(Array * addresses) +{ + struct mailimf_mailbox_list * mb_list; + + if (addresses == NULL) + return NULL; + + if (addresses->count() == 0) + return NULL; + + mb_list = mailimf_mailbox_list_new_empty(); + + for(unsigned i = 0 ; i < addresses->count() ; i ++) { + Address * address = (Address *) addresses->objectAtIndex(i); + struct mailimf_mailbox * mailbox = address->createIMFMailbox(); + mailimf_mailbox_list_add(mb_list, mailbox); + } + + return mb_list; +} + +static struct mailimf_address_list * lep_address_list_from_array(Array * addresses) +{ + struct mailimf_address_list * addr_list; + + if (addresses == NULL) + return NULL; + + if (addresses->count() == 0) + return NULL; + + addr_list = mailimf_address_list_new_empty(); + + for(unsigned i = 0 ; i < addresses->count() ; i ++) { + Address * address = (Address *) addresses->objectAtIndex(i); + struct mailimf_address * addr = address->createIMFAddress(); + mailimf_address_list_add(addr_list, addr); + } + + return addr_list; +} + +#pragma mark Message-ID conversion + +static Array * msg_id_to_string_array(clist * msgids) +{ + clistiter * cur; + Array * result; + + result = Array::array(); + + for(cur = clist_begin(msgids) ; cur != NULL ; cur = clist_next(cur)) { + char * msgid; + String * str; + + msgid = (char *) clist_content(cur); + str = String::stringWithUTF8Characters(msgid); + result->addObject(str); + } + + return result; +} + +static clist * msg_id_from_string_array(Array * msgids) +{ + clist * result; + + if (msgids == NULL) + return NULL; + + if (msgids->count() == 0) + return NULL; + + result = clist_new(); + for(unsigned int i = 0 ; i < msgids->count() ; i ++) { + String * msgid = (String *) msgids->objectAtIndex(i); + clist_append(result, strdup(msgid->UTF8Characters())); + } + + return result; +} + +struct mailimf_fields * MessageHeader::createIMFFieldsAndFilterBcc(bool filterBcc) +{ + struct mailimf_date_time * imfDate; + char * imfMsgid; + char * imfSubject; + struct mailimf_mailbox_list * imfFrom; + struct mailimf_address_list * imfReplyTo; + struct mailimf_address_list * imfTo; + struct mailimf_address_list * imfCc; + struct mailimf_address_list * imfBcc; + clist * imfInReplyTo; + clist * imfReferences; + struct mailimf_fields * fields; + + imfDate = NULL; + if (date() != (time_t) -1) { + MCLog("%lu", date()); + imfDate = get_date_from_timestamp(date()); + } + imfFrom = NULL; + if (from() != NULL) { + imfFrom = lep_mailbox_list_from_array(Array::arrayWithObject(from())); + } + imfReplyTo = lep_address_list_from_array(replyTo()); + imfTo = lep_address_list_from_array(to()); + imfCc = lep_address_list_from_array(cc()); + imfBcc = NULL; + if (!filterBcc) { + imfBcc = lep_address_list_from_array(bcc()); + } + imfMsgid = NULL; + if (messageID() != NULL) { + imfMsgid = strdup(messageID()->UTF8Characters()); + } + imfInReplyTo = msg_id_from_string_array(inReplyTo()); + imfReferences = msg_id_from_string_array(references()); + imfSubject = NULL; + if ((subject() != NULL) && (subject()->length() > 0)) { + Data * data; + + data = subject()->encodedMIMEHeaderValueForSubject(); + if (data->bytes() != NULL) { + imfSubject = strdup(data->bytes()); + } + } + + if ((imfTo == NULL) && (imfCc == NULL) && (imfBcc == NULL)) { + imfTo = mailimf_address_list_new_empty(); + mailimf_address_list_add_parse(imfTo, (char *) "Undisclosed recipients:;"); + } + + fields = mailimf_fields_new_with_data_all(imfDate, + imfFrom, + NULL /* sender */, + imfReplyTo, + imfTo, + imfCc, + imfBcc, + imfMsgid, + imfInReplyTo, + imfReferences, + imfSubject); + + if (mUserAgent != NULL) { + struct mailimf_field * field; + + field = mailimf_field_new_custom(strdup("X-Mailer"), strdup(mUserAgent->UTF8Characters())); + mailimf_fields_add(fields, field); + } + + return fields; +} + +extern "C" { + extern int mailimap_hack_date_time_parse(char * str, + struct mailimap_date_time ** result, + size_t progr_rate, + progress_function * progr_fun); +} + +#pragma mark IMAP mailbox conversion + +static Array * imap_mailbox_list_to_address_array(clist * imap_mailbox_list) +{ + clistiter * cur; + Array * result; + + result = Array::array(); + + for(cur = clist_begin(imap_mailbox_list) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimap_address * imap_addr; + Address * address; + + imap_addr = (struct mailimap_address *) clist_content(cur); + address = Address::addressWithIMAPAddress(imap_addr); + result->addObject(address); + } + + return result; +} + +void MessageHeader::importIMAPEnvelope(struct mailimap_envelope * env) +{ + if (env->env_date != NULL) { + size_t cur_token; + struct mailimf_date_time * date_time; + int r; + + cur_token = 0; + r = mailimf_date_time_parse(env->env_date, strlen(env->env_date), + &cur_token, &date_time); + if (r == MAILIMF_NO_ERROR) { + time_t timestamp; + + // date + timestamp = timestamp_from_date(date_time); + setDate(timestamp); + setReceivedDate(timestamp); + mailimf_date_time_free(date_time); + } + else { + struct mailimap_date_time * imap_date; + + r = mailimap_hack_date_time_parse(env->env_date, &imap_date, 0, NULL); + if (r == MAILIMAP_NO_ERROR) { + time_t timestamp; + + timestamp = timestamp_from_imap_date(imap_date); + setDate(timestamp); + setReceivedDate(timestamp); + mailimap_date_time_free(imap_date); + } + } + } + + if (env->env_subject != NULL) { + char * subject; + + // subject + subject = env->env_subject; + setSubject(String::stringByDecodingMIMEHeaderValue(subject)); + } + + if (env->env_sender != NULL) { + if (env->env_sender->snd_list != NULL) { + Array * addresses; + + addresses = imap_mailbox_list_to_address_array(env->env_sender->snd_list); + if (addresses->count() > 0) { + setSender((Address *) addresses->objectAtIndex(0)); + } + } + } + + if (env->env_from != NULL) { + if (env->env_from->frm_list != NULL) { + Array * addresses; + + addresses = imap_mailbox_list_to_address_array(env->env_from->frm_list); + if (addresses->count() > 0) { + setFrom((Address *) addresses->objectAtIndex(0)); + } + } + } + + // skip Sender header + + if (env->env_reply_to != NULL) { + if (env->env_reply_to->rt_list != NULL) { + Array * addresses; + + addresses = imap_mailbox_list_to_address_array(env->env_reply_to->rt_list); + setReplyTo(addresses); + } + } + + if (env->env_to != NULL) { + if (env->env_to->to_list != NULL) { + Array * addresses; + + addresses = imap_mailbox_list_to_address_array(env->env_to->to_list); + setTo(addresses); + } + } + + if (env->env_cc != NULL) { + if (env->env_cc->cc_list != NULL) { + Array * addresses; + + addresses = imap_mailbox_list_to_address_array(env->env_cc->cc_list); + setCc(addresses); + } + } + + if (env->env_bcc != NULL) { + if (env->env_bcc->bcc_list != NULL) { + Array * addresses; + + addresses = imap_mailbox_list_to_address_array(env->env_bcc->bcc_list); + setBcc(addresses); + } + } + + if (env->env_in_reply_to != NULL) { + size_t cur_token; + clist * msg_id_list; + int r; + + cur_token = 0; + r = mailimf_msg_id_list_parse(env->env_in_reply_to, + strlen(env->env_in_reply_to), &cur_token, &msg_id_list); + if (r == MAILIMF_NO_ERROR) { + Array * msgids; + + msgids = msg_id_to_string_array(msg_id_list); + setInReplyTo(msgids); + // in-reply-to + clist_foreach(msg_id_list, (clist_func) mailimf_msg_id_free, NULL); + clist_free(msg_id_list); + } + } + + if (env->env_message_id != NULL) { + char * msgid; + size_t cur_token; + int r; + + cur_token = 0; + r = mailimf_msg_id_parse(env->env_message_id, strlen(env->env_message_id), + &cur_token, &msgid); + if (r == MAILIMF_NO_ERROR) { + // msg id + String * str; + + str = String::stringWithUTF8Characters(msgid); + setMessageID(str); + mailimf_msg_id_free(msgid); + } + } +} + +void MessageHeader::importIMAPReferences(Data * data) +{ + size_t cur_token; + struct mailimf_fields * fields; + int r; + struct mailimf_single_fields single_fields; + + cur_token = 0; + r = mailimf_fields_parse(data->bytes(), data->length(), &cur_token, &fields); + if (r != MAILIMF_NO_ERROR) { + return; + } + + mailimf_single_fields_init(&single_fields, fields); + if (single_fields.fld_references != NULL) { + Array * msgids; + + msgids = msg_id_to_string_array(single_fields.fld_references->mid_list); + setReferences(msgids); + } + if (single_fields.fld_subject != NULL) { + if (single_fields.fld_subject->sbj_value != NULL) { + bool broken; + char * value; + bool isASCII; + + broken = false; + value = single_fields.fld_subject->sbj_value; + + isASCII = true; + for(char * p = value ; * p != 0 ; p ++) { + if ((unsigned char) * p >= 128) { + isASCII = false; + } + } + if (strstr(value, "windows-1251") == NULL) { + if (isASCII) { + broken = true; + } + } + + //MCLog("charset: %s %s", value, MCUTF8(charset)); + + if (!broken) { + setSubject(String::stringByDecodingMIMEHeaderValue(single_fields.fld_subject->sbj_value)); + } + } + } + + mailimf_fields_free(fields); +} + +void MessageHeader::importIMAPInternalDate(struct mailimap_date_time * date) +{ + setReceivedDate(timestamp_from_imap_date(date)); +} + diff --git a/src/core/abstract/MCMessageHeader.h b/src/core/abstract/MCMessageHeader.h new file mode 100644 index 00000000..5a7483e2 --- /dev/null +++ b/src/core/abstract/MCMessageHeader.h @@ -0,0 +1,90 @@ +#ifndef __MAILCORE_MCMESSAGEHEADER_H_ + +#define __MAILCORE_MCMESSAGEHEADER_H_ + +#include <mailcore/MCBaseTypes.h> +#include <time.h> + +namespace mailcore { + + class Address; + + class MessageHeader : public Object { + private: + String * mMessageID; + Array * /* String */ mReferences; + Array * /* String */ mInReplyTo; + Address * mSender; + Address * mFrom; + Array * /* Address */ mTo; + Array * /* Address */ mCc; + Array * /* Address */ mBcc; + Array * /* Address */ mReplyTo; + String * mSubject; + time_t mDate; + time_t mReceivedDate; + String * mUserAgent; + void init(bool generateDate, bool generateMessageID); + + public: + MessageHeader(); + MessageHeader(MessageHeader * other); + virtual ~MessageHeader(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual void setMessageID(String * messageID); + virtual String * messageID(); + + virtual void setReferences(Array * references); + virtual Array * references(); + + virtual void setInReplyTo(Array * inReplyTo); + virtual Array * inReplyTo(); + + virtual void setDate(time_t date); + virtual time_t date(); + + virtual void setReceivedDate(time_t date); + virtual time_t receivedDate(); + + virtual void setSender(Address * sender); + virtual Address * sender(); + + virtual void setFrom(Address * from); + virtual Address * from(); + + virtual void setTo(Array * to); + virtual Array * to(); + + virtual void setCc(Array * cc); + virtual Array * cc(); + + virtual void setBcc(Array * bcc); + virtual Array * bcc(); + + virtual void setReplyTo(Array * replyTo); + virtual Array * replyTo(); + + virtual void setSubject(String * subject); + virtual String * subject(); + + virtual void setUserAgent(String * userAgent); + virtual String * userAgent(); + + virtual String * extractedSubject(); + virtual String * partialExtractedSubject(); + virtual void importHeadersData(Data * data); + virtual void importIMAPEnvelope(struct mailimap_envelope * env); + virtual void importIMAPReferences(Data * data); + virtual void importIMAPInternalDate(struct mailimap_date_time * date); + + virtual struct mailimf_fields * createIMFFieldsAndFilterBcc(bool filterBcc); + virtual void importIMFFields(struct mailimf_fields * fields); + }; + +} + +#endif diff --git a/src/core/basetypes/.DS_Store b/src/core/basetypes/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/src/core/basetypes/.DS_Store diff --git a/src/core/basetypes/MCArray.cc b/src/core/basetypes/MCArray.cc new file mode 100644 index 00000000..6b4df259 --- /dev/null +++ b/src/core/basetypes/MCArray.cc @@ -0,0 +1,229 @@ +#include "MCArray.h" + +#include <string.h> +#include <stdlib.h> + +#include "MCAssert.h" +#include "MCString.h" +#include "MCLog.h" +#include "MCUtils.h" + +using namespace mailcore; + +Array::Array() +{ + init(); +} + +Array::Array(Array * other) : Object() +{ + init(); + for(unsigned int i = 0 ; i < other->count() ; i ++) { + Object * obj = other->objectAtIndex(i); + addObject(obj); + } +} + +void Array::init() +{ + mArray = carray_new(4); +} + +Array::~Array() +{ + removeAllObjects(); + carray_free(mArray); +} + +Array * Array::array() +{ + Array * result = new Array(); + return (Array *) result->autorelease(); +} + +Array * Array::arrayWithObject(Object * obj) +{ + Array * result = new Array(); + result->addObject(obj); + return (Array *) result->autorelease(); +} + +#if 0 +String * Array::className() +{ + return MCSTR("Array"); +} +#endif + +String * Array::description() +{ + String * result = String::string(); + + result->appendUTF8Characters("["); + for(unsigned int i = 0 ; i < count() ; i ++) { + if (i != 0) { + result->appendUTF8Characters(","); + } + Object * obj = objectAtIndex(i); + result->appendString(obj->description()); + } + result->appendUTF8Characters("]"); + + return result; +} + +Object * Array::copy() +{ + return new Array(this); +} + +unsigned int Array::count() +{ + return carray_count(mArray); +} + +void Array::addObject(Object * obj) +{ + unsigned int idx; + obj->retain(); + carray_add(mArray, obj, &idx); +} + +void Array::removeObjectAtIndex(unsigned int idx) +{ + Object * obj = (Object *) carray_get(mArray, idx); + obj->release(); + carray_delete_slow(mArray, idx); +} + +void Array::removeObject(Object * obj) +{ + unsigned int idx = indexOfObject(obj); + removeObjectAtIndex(idx); +} + +int Array::indexOfObject(Object * obj) +{ + for(unsigned int i = 0 ; i < count() ; i ++) { + Object * currentObj = objectAtIndex(i); + if (currentObj->isEqual(obj)) { + return i; + } + } + + return -1; +} + +Object * Array::objectAtIndex(unsigned int idx) +{ + Object * obj = (Object *) carray_get(mArray, idx); + return obj; +} + +void Array::replaceObject(unsigned int idx, Object * obj) +{ + if (idx < count()) { + Object * previousObject = (Object *) carray_get(mArray, idx); + previousObject->release(); + obj->retain(); + carray_set(mArray, idx, obj); + } + else if (idx == count()) { + addObject(obj); + } + else { + MCAssert(0); + } +} + +void Array::insertObject(unsigned int idx, Object * obj) +{ + if (idx < count()) { + int count = carray_count(mArray) - idx; + carray_set_size(mArray, count + 1); + void ** p = carray_data(mArray); + memmove(p + idx + 1, p + idx, count * sizeof(* p)); + obj->retain(); + carray_set(mArray, idx, obj); + } + else if (idx == count()) { + addObject(obj); + } + else { + MCAssert(0); + } +} + +void Array::removeAllObjects() +{ + for(unsigned int i = 0 ; i < count() ; i ++) { + Object * obj = objectAtIndex(i); + obj->release(); + } + carray_set_size(mArray, 0); +} + +void Array::addObjectsFromArray(Array * array) +{ + if (array == NULL) + return; + + for(unsigned int i = 0 ; i < array->count() ; i ++) { + Object * obj = array->objectAtIndex(i); + addObject(obj); + } +} + +Object * Array::lastObject() +{ + if (count() == 0) + return NULL; + + return objectAtIndex(count() - 1); +} + +bool Array::containsObject(Object * obj) +{ + return (indexOfObject(obj) != -1); +} + +struct sortData { + int (* compare)(void *, void *, void *); + void * context; +}; + +static int sortCompare(struct sortData * data, Object ** pa, Object ** pb) +{ + Object * a; + Object * b; + + a = * pa; + b = * pb; + + return data->compare(a, b, data->context); +} + +Array * Array::sortedArray(int (* compare)(void *, void *, void *), void * context) +{ + struct sortData data; + Array * result = (Array *) this->copy()->autorelease(); + data.compare = compare; + data.context = context; + qsort_r(carray_data(result->mArray), carray_count(result->mArray), + sizeof(* carray_data(result->mArray)), this, + (int (*)(void *, const void *, const void *)) sortCompare); + return result; +} + +String * Array::componentsJoinedByString(String * delimiter) +{ + String * result = String::string(); + for(unsigned int i = 0 ; i < count() ; i ++) { + Object * obj = objectAtIndex(i); + if (result != 0) { + result->appendString(delimiter); + } + result->appendString(obj->description()); + } + return result; +} diff --git a/src/core/basetypes/MCArray.h b/src/core/basetypes/MCArray.h new file mode 100644 index 00000000..a0abc243 --- /dev/null +++ b/src/core/basetypes/MCArray.h @@ -0,0 +1,49 @@ +#ifndef __MAILCORE_MCARRAY_H_ + +#define __MAILCORE_MCARRAY_H_ + +#include <mailcore/MCObject.h> + +#include <libetpan/libetpan.h> + +namespace mailcore { + + class String; + + class Array : public Object { + private: + carray * mArray; + void init(); + public: + Array(); + Array(Array * o); + virtual ~Array(); + + static Array * array(); + static Array * arrayWithObject(Object * obj); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + + virtual unsigned int count(); + virtual void addObject(Object * obj); + virtual void removeObjectAtIndex(unsigned int idx); + virtual void removeObject(Object * obj); + virtual int indexOfObject(Object * obj); + virtual Object * objectAtIndex(unsigned int idx); + virtual void replaceObject(unsigned int idx, Object * obj); + virtual void insertObject(unsigned int idx, Object * obj); + virtual void removeAllObjects(); + + virtual void addObjectsFromArray(Array * array); + virtual Object * lastObject(); + virtual bool containsObject(Object * obj); + + virtual Array * sortedArray(int (* compare)(void *, void *, void *), void * context); + virtual String * componentsJoinedByString(String * delimiter); + }; + +} + +#endif diff --git a/src/core/basetypes/MCAssert.cc b/src/core/basetypes/MCAssert.cc new file mode 100644 index 00000000..afe9bfd5 --- /dev/null +++ b/src/core/basetypes/MCAssert.cc @@ -0,0 +1,12 @@ +#include "MCAssert.h" + +#include <stdio.h> + +void mailcore::assertInteral(const char * filename, unsigned int line, int cond, const char * condString) +{ + if (cond) { + return; + } + + fprintf(stderr, "%s:%i: assert %s\n", filename, line, condString); +} diff --git a/src/core/basetypes/MCAssert.h b/src/core/basetypes/MCAssert.h new file mode 100644 index 00000000..cc13aba8 --- /dev/null +++ b/src/core/basetypes/MCAssert.h @@ -0,0 +1,13 @@ +#ifndef __MAILCORE_MCASSERT_H_ + +#define __MAILCORE_MCASSERT_H_ + +#define MCAssert(cond) mailcore::assertInteral(__FILE__, __LINE__, cond, #cond) + +namespace mailcore { + + void assertInteral(const char * filename, unsigned int line, int cond, const char * condString); + +} + +#endif diff --git a/src/core/basetypes/MCAutoreleasePool.cc b/src/core/basetypes/MCAutoreleasePool.cc new file mode 100644 index 00000000..7e43734f --- /dev/null +++ b/src/core/basetypes/MCAutoreleasePool.cc @@ -0,0 +1,119 @@ +#include "MCAutoreleasePool.h" + +#include "MCString.h" +#include "MCLog.h" +#include "MCUtils.h" + +using namespace mailcore; + +pthread_key_t AutoreleasePool::autoreleasePoolStackKey; + +void AutoreleasePool::init() +{ + static pthread_once_t once = PTHREAD_ONCE_INIT; + pthread_once(&once, initAutoreleasePoolStackKey); +} + +AutoreleasePool::AutoreleasePool() +{ + mPoolObjects = carray_new(4); + + unsigned int idx; + carray * stack = createAutoreleasePoolStackIfNeeded(); + carray_add(stack, this, &idx); +} + +AutoreleasePool::~AutoreleasePool() +{ + carray * stack = createAutoreleasePoolStackIfNeeded(); + carray_delete_slow(stack, carray_count(stack) - 1); + + unsigned int count = carray_count(mPoolObjects); + for(unsigned int i = 0 ; i < count ; i ++) { + Object * obj = (Object *) carray_get(mPoolObjects, i); + obj->release(); + } + carray_free(mPoolObjects); +} + +carray * AutoreleasePool::createAutoreleasePoolStackIfNeeded() +{ + init(); + carray * stack = (carray *) pthread_getspecific(autoreleasePoolStackKey); + if (stack != NULL) { + return stack; + } + + stack = carray_new(4); + pthread_setspecific(autoreleasePoolStackKey, stack); + + return stack; +} + +void AutoreleasePool::destroyAutoreleasePoolStack(void *) +{ + init(); + carray * stack = (carray *) pthread_getspecific(autoreleasePoolStackKey); + if (carray_count(stack) != 0) { + MCLog("some autoreleasepool have not been released\n"); + } + carray_free(stack); +} + +void AutoreleasePool::initAutoreleasePoolStackKey() +{ + pthread_key_create(&autoreleasePoolStackKey, destroyAutoreleasePoolStack); +} + +AutoreleasePool * AutoreleasePool::currentAutoreleasePool() +{ + init(); + carray * stack; + stack = createAutoreleasePoolStackIfNeeded(); + if (carray_count(stack) == 0) { + //fprintf(stderr, "no current autoreleasepool\n"); + return NULL; + } + + AutoreleasePool * pool; + pool = (AutoreleasePool *) carray_get(stack, carray_count(stack) - 1); + return pool; +} + +void AutoreleasePool::add(Object * obj) +{ + unsigned int idx; + carray_add(mPoolObjects, obj, &idx); +} + +void AutoreleasePool::autorelease(Object * obj) +{ + AutoreleasePool * pool = AutoreleasePool::currentAutoreleasePool(); + if (pool == NULL) { + MCLog("-autorelease called with no current autoreleasepool\n"); + return; + } + pool->add(obj); +} + +String * AutoreleasePool::className() +{ + return MCSTR("AutoreleasePool"); +} + +String * AutoreleasePool::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%p:%p ", className(), this); + unsigned int count = carray_count(mPoolObjects); + for(unsigned int i = 0 ; i < count ; i ++) { + Object * obj = (Object *) carray_get(mPoolObjects, i); + if (i != 0) { + result->appendUTF8Characters(" "); + } + result->appendString(obj->description()); + } + result->appendUTF8Characters(">"); + + return result; +} diff --git a/src/core/basetypes/MCAutoreleasePool.h b/src/core/basetypes/MCAutoreleasePool.h new file mode 100644 index 00000000..ee30de87 --- /dev/null +++ b/src/core/basetypes/MCAutoreleasePool.h @@ -0,0 +1,34 @@ +#ifndef __MAILCORE_MCAUTORELEASEPOOL_H_ + +#define __MAILCORE_MCAUTORELEASEPOOL_H_ + +#include <mailcore/MCObject.h> +#include <libetpan/libetpan.h> +#include <pthread.h> + +namespace mailcore { + + class AutoreleasePool : public Object { + private: + static void init(); + static pthread_key_t autoreleasePoolStackKey; + carray * mPoolObjects; + static carray * createAutoreleasePoolStackIfNeeded(); + static void destroyAutoreleasePoolStack(void *); + static void initAutoreleasePoolStackKey(); + static AutoreleasePool * currentAutoreleasePool(); + virtual void add(Object * obj); + + public: + AutoreleasePool(); + virtual ~AutoreleasePool(); + + virtual String * className(); + virtual String * description(); + + static void autorelease(Object * obj); + }; + +} + +#endif diff --git a/src/core/basetypes/MCBaseTypes.h b/src/core/basetypes/MCBaseTypes.h new file mode 100644 index 00000000..0f86dfb7 --- /dev/null +++ b/src/core/basetypes/MCBaseTypes.h @@ -0,0 +1,21 @@ +#ifndef __MAILCORE_MCBASETYPES_H_ + +#define __MAILCORE_MCBASETYPES_H_ + +#include <mailcore/MCAutoreleasePool.h> +#include <mailcore/MCObject.h> +#include <mailcore/MCValue.h> +#include <mailcore/MCString.h> +#include <mailcore/MCData.h> +#include <mailcore/MCArray.h> +#include <mailcore/MCHashMap.h> +#include <mailcore/MCHash.h> +#include <mailcore/MCLog.h> +#include <mailcore/MCAssert.h> +#include <mailcore/MCUtils.h> +#include <mailcore/MCRange.h> +#include <mailcore/MCOperation.h> +#include <mailcore/MCOperationQueue.h> +#include <mailcore/MCOperationCallback.h> + +#endif diff --git a/src/core/basetypes/MCData.cc b/src/core/basetypes/MCData.cc new file mode 100644 index 00000000..4550bff8 --- /dev/null +++ b/src/core/basetypes/MCData.cc @@ -0,0 +1,526 @@ +#include "MCData.h" + +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unicode/ucsdet.h> +#include <libetpan/libetpan.h> + +#include "MCString.h" +#include "MCHash.h" +#include "MCUtils.h" + +#define DEFAULT_CHARSET "iso-8859-1" + +using namespace mailcore; + +void Data::allocate(unsigned int length) +{ + length ++; + if (length < mAllocated) + return; + + if (mAllocated == 0) { + mAllocated = 4; + } + while (length > mAllocated) { + mAllocated *= 2; + } + + mBytes = (char *) realloc(mBytes, mAllocated); +} + +void Data::reset() +{ + free(mBytes); + mAllocated = 0; + mLength = 0; + mBytes = NULL; +} + +Data::Data() +{ + mBytes = NULL; + reset(); +} + +Data::Data(Data * otherData) : Object() +{ + mBytes = NULL; + reset(); + appendData(otherData); +} + +Data::Data(const char * bytes, unsigned int length) +{ + mBytes = NULL; + reset(); + appendBytes(bytes, length); +} + +Data::Data(int capacity) +{ + mBytes = NULL; + reset(); + allocate(capacity); +} + +Data::~Data() +{ + reset(); +} + +Data * Data::dataWithBytes(const char * bytes, unsigned int length) +{ + Data * result = new Data(bytes, length); + return (Data *) result->autorelease(); +} + +char * Data::bytes() +{ + return mBytes; +} + +unsigned int Data::length() +{ + return mLength; +} + +void Data::appendData(Data * otherData) +{ + appendBytes(otherData->bytes(), otherData->length()); +} + +void Data::appendBytes(const char * bytes, unsigned int length) +{ + allocate(mLength + length); + memcpy(&mBytes[mLength], bytes, length); + mLength += length; +} + +void Data::setBytes(const char * bytes, unsigned int length) +{ + reset(); + appendBytes(bytes, length); +} + +void Data::setData(Data * otherData) +{ + reset(); + appendData(otherData); +} + +#if 0 +String * Data::className() +{ + return MCSTR("Data"); +} +#endif + +String * Data::description() +{ + return String::stringWithUTF8Format("<%s:%p %i bytes>", MCUTF8(className()), this, length()); +} + +Object * Data::copy() +{ + return new Data(this); +} + +bool Data::isEqual(Object * otherObject) +{ + Data * otherData = (Data *) otherObject; + if (length() != otherData->length()) + return false; + if (memcmp(bytes(), otherData->bytes(), mLength) != 0) + return false; + return true; +} + +unsigned int Data::hash() +{ + return hashCompute(mBytes, mLength); +} + +String * Data::stringWithDetectedCharset() +{ + String * charset; + String * result; + + charset = charsetWithFilteredHTML(false); + if (charset == NULL) { + charset = MCSTR(DEFAULT_CHARSET); + } + result = emailStringWithCharset(charset); + if (result == NULL) { + result = stringWithCharset("utf-8"); + } + return result; +} + +String * Data::normalizeCharset(String * charset) +{ + if ((charset->caseInsensitiveCompare(MCSTR("iso-2022-jp")) == 0) || + (charset->caseInsensitiveCompare(MCSTR("iso-2022-jp-2")) == 0)) { + charset = MCSTR("iso-2022-jp-2"); + } + else if (charset->caseInsensitiveCompare(MCSTR("ks_c_5601-1987")) == 0) { + charset = MCSTR("euckr"); + } + else if ((charset->caseInsensitiveCompare(MCSTR("iso-8859-8-i")) == 0) || + (charset->caseInsensitiveCompare(MCSTR("iso-8859-8-e")) == 0)) { + charset = MCSTR("iso-8859-8"); + } + else if ((charset->caseInsensitiveCompare(MCSTR("GB2312")) == 0) || + (charset->caseInsensitiveCompare(MCSTR("GB_2312-80")) == 0)) { + charset = MCSTR("GBK"); + } + + return charset->lowercaseString(); +} + +String * Data::stringWithCharset(const char * charset) +{ + String * result = new String(this, charset); + return (String *) result->autorelease(); +} + +String * Data::emailStringWithCharset(String * charset) +{ + String * result; + + charset = normalizeCharset(charset); + + if (charset->isEqual(MCSTR("iso-2022-jp-2"))) { + const char * theBytes; + Data * data; + + theBytes = bytes(); + data = this; + if (length() > 0) { + unsigned int idx; + + idx = length(); + while ((theBytes[idx - 1] == '\n') || (theBytes[idx - 1] == '\r')) { + idx --; + if (idx == 0) + break; + } + + if (idx != length()) { + data = Data::dataWithBytes(theBytes, idx); + } + } + result = data->stringWithCharset("iso-2022-jp-2"); + if (result == NULL) { + result = data->stringWithCharset("iso-2022-jp"); + } + + return result; + } + + result = stringWithCharset(charset->UTF8Characters()); + return result; +} + +String * Data::charsetWithFilteredHTMLWithoutHint(bool filterHTML) +{ + UCharsetDetector * detector; + const UCharsetMatch * match; + UErrorCode err = U_ZERO_ERROR; + const char * cName; + String * result; + + detector = ucsdet_open(&err); + ucsdet_setText(detector, bytes(), length(), &err); + ucsdet_enableInputFilter(detector, filterHTML); + match = ucsdet_detect(detector, &err); + if (match == NULL) { + ucsdet_close(detector); + return NULL; + } + + cName = ucsdet_getName(match, &err); + + result = String::stringWithUTF8Characters(cName); + ucsdet_close(detector); + + return result; +} + +String * Data::charsetWithFilteredHTML(bool filterHTML, String * hintCharset) +{ + if (hintCharset == NULL) + return charsetWithFilteredHTMLWithoutHint(filterHTML); + + const UCharsetMatch ** matches; + int32_t matchesCount; + UCharsetDetector * detector; + UErrorCode err = U_ZERO_ERROR; + String * result; + + hintCharset = hintCharset->lowercaseString(); + + detector = ucsdet_open(&err); + ucsdet_setText(detector, bytes(), length(), &err); + ucsdet_enableInputFilter(detector, filterHTML); + matches = ucsdet_detectAll(detector, &matchesCount, &err); + if (matches == NULL) { + ucsdet_close(detector); + return hintCharset; + } + if (matchesCount == 0) { + ucsdet_close(detector); + return hintCharset; + } + + result = NULL; + + for(int32_t i = 0 ; i < matchesCount ; i ++) { + const char * cName; + String * name; + int32_t confidence; + + cName = ucsdet_getName(matches[i], &err); + name = String::stringWithUTF8Characters(cName); + name = name->lowercaseString(); + confidence = ucsdet_getConfidence(matches[i], &err); + if ((confidence >= 50) && name->isEqual(hintCharset)) { + result = name; + break; + } + } + + if (result == NULL) { + int32_t maxConfidence; + + maxConfidence = 49; + + for(int32_t i = 0 ; i < matchesCount ; i ++) { + const char * cName; + String * name; + int32_t confidence; + + cName = ucsdet_getName(matches[i], &err); + confidence = ucsdet_getConfidence(matches[i], &err); + name = String::stringWithUTF8Characters(cName); + if (confidence > maxConfidence) { + result = name; + maxConfidence = confidence; + } + } + } + ucsdet_close(detector); + + if (result == NULL) + result = hintCharset; + + return result; +} + +Data * Data::dataWithContentsOfFile(String * filename) +{ + int r; + size_t read_items; + struct stat stat_buf; + FILE * f; + char * buf; + Data * data; + + f = fopen(filename->fileSystemRepresentation(), "rb"); + if (f == NULL) { + return NULL; + } + + r = fstat(fileno(f), &stat_buf); + if (r < 0) { + fclose(f); + return NULL; + } + + buf = (char *) malloc(stat_buf.st_size); + + read_items = fread(buf, 1, stat_buf.st_size, f); + if ((off_t) read_items != stat_buf.st_size) { + free(buf); + fclose(f); + return NULL; + } + + data = Data::dataWithBytes(buf, (unsigned int) stat_buf.st_size); + free(buf); + + fclose(f); + + return data; +} + +static size_t uudecode(char * text, size_t size) +{ + unsigned int count = 0; + char *b = text; /* beg */ + char *s = b; /* src */ + char *d = b; /* dst */ + char *e = b+size; /* end */ + int out = (*s++ & 0x7f) - 0x20; + + /* don't process lines without leading count character */ + if (out < 0) + 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) + { + 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; + } + *d = (char) '\0'; + return count; +} + +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::dataWithBytes(decoded, (unsigned int) decoded_length); + mailmime_decoded_part_free(decoded); + return data; + } + case EncodingUUEncode: + { + char * dup_data; + size_t decoded_length; + Data * data; + char * current_p; + + data = Data::dataWithCapacity((unsigned int) text_length); + + dup_data = (char *) malloc(text_length); + memcpy(dup_data, text, text_length); + + current_p = dup_data; + while (1) { + size_t length; + char * p; + char * p1; + char * p2; + 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 - dup_data) < text_length) { + if ((* p != '\r') && (* p != '\n')) { + break; + } + p ++; + } + } + if (p == NULL) { + length = text_length - (current_p - dup_data); + } + else { + length = end_line - current_p; + } + if (length == 0) { + break; + } + decoded_length = uudecode(current_p, length); + if (decoded_length != 0 && decoded_length < length) { + data->appendBytes(current_p, (unsigned int) decoded_length); + } + + if (p == NULL) + break; + + current_p = p; + while ((size_t) (current_p - dup_data) < text_length) { + if ((* current_p != '\r') && (* current_p != '\n')) { + break; + } + current_p ++; + } + } + free(dup_data); + + return data; + } + } +} + +Data * Data::data() +{ + return dataWithCapacity(0); +} + +Data * Data::dataWithCapacity(int capacity) +{ + Data * result = new Data(capacity); + return (Data *) result->autorelease(); +} diff --git a/src/core/basetypes/MCData.h b/src/core/basetypes/MCData.h new file mode 100644 index 00000000..52232383 --- /dev/null +++ b/src/core/basetypes/MCData.h @@ -0,0 +1,58 @@ +#ifndef __MAILCORE_MCDATA_H_ + +#define __MAILCORE_MCDATA_H_ + +#include <mailcore/MCObject.h> +#include <mailcore/MCMessageConstants.h> + +namespace mailcore { + + class String; + + class Data : public Object { + private: + char * mBytes; + unsigned int mLength; + unsigned int mAllocated; + void allocate(unsigned int length); + void reset(); + static String * normalizeCharset(String * charset); + String * charsetWithFilteredHTMLWithoutHint(bool filterHTML); + + public: + Data(); + Data(int capacity); + Data(Data * otherData); + Data(const char * bytes, unsigned int length); + virtual ~Data(); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + virtual bool isEqual(Object * otherObject); + virtual unsigned int hash(); + + static Data * data(); + static Data * dataWithCapacity(int capacity); + static Data * dataWithContentsOfFile(String * filename); + static Data * dataWithBytes(const char * bytes, unsigned int length); + + virtual char * bytes(); + virtual unsigned int length(); + + virtual void appendData(Data * otherData); + virtual void appendBytes(const char * bytes, unsigned int length); + virtual void setBytes(const char * bytes, unsigned int length); + virtual void setData(Data * otherData); + + // Helpers + virtual String * stringWithDetectedCharset(); + virtual String * emailStringWithCharset(String * charset); + virtual String * stringWithCharset(const char * charset); + virtual String * charsetWithFilteredHTML(bool filterHTML, String * hintCharset = NULL); + virtual Data * decodedDataUsingEncoding(Encoding encoding); + }; + +} + +#endif diff --git a/src/core/basetypes/MCHash.cc b/src/core/basetypes/MCHash.cc new file mode 100644 index 00000000..116557e5 --- /dev/null +++ b/src/core/basetypes/MCHash.cc @@ -0,0 +1,12 @@ +#include "MCHash.h" + +unsigned int mailcore::hashCompute(const char * key, unsigned int len) { + register unsigned int c = 5381; + register const char * k = key; + + while (len--) { + c = ((c << 5) + c) + *k++; + } + + return c; +} diff --git a/src/core/basetypes/MCHash.h b/src/core/basetypes/MCHash.h new file mode 100644 index 00000000..ecce9639 --- /dev/null +++ b/src/core/basetypes/MCHash.h @@ -0,0 +1,11 @@ +#ifndef __MAILCORE_MCHASH_H_ + +#define __MAILCORE_MCHASH_H_ + +namespace mailcore { + + unsigned int hashCompute(const char * key, unsigned int len); + +} + +#endif diff --git a/src/core/basetypes/MCHashMap.cc b/src/core/basetypes/MCHashMap.cc new file mode 100644 index 00000000..8dcf0f30 --- /dev/null +++ b/src/core/basetypes/MCHashMap.cc @@ -0,0 +1,283 @@ +#include "MCHashMap.h" + +#include <stdlib.h> +#include <string.h> + +#include "MCArray.h" +#include "MCString.h" +#include "MCUtils.h" +#include "MCLog.h" + +using namespace mailcore; + +namespace mailcore { + struct HashMapCell { + unsigned int func; + Object * key; + Object * value; + HashMapCell * next; + }; +} + +#define CHASH_DEFAULTSIZE 13 +#define CHASH_MAXDEPTH 3 + +void HashMap::init() +{ + mCount = 0; + mCells = (void **) (HashMapCell **) calloc(CHASH_DEFAULTSIZE, sizeof(HashMapCell *)); + mAllocated = CHASH_DEFAULTSIZE; +} + +HashMap::HashMap() +{ + init(); +} + +HashMap::HashMap(HashMap * other) +{ + init(); + Array * keys = other->allKeys(); + for(unsigned int i = 0 ; i < keys->count() ; i ++) { + Object * key = keys->objectAtIndex(i); + Object * value = other->objectForKey(key); + setObjectForKey(key, value); + } +} + +HashMap::~HashMap() +{ + for(unsigned int indx = 0; indx < mAllocated; indx++) { + HashMapIter * iter, * next; + iter = (HashMapIter *) mCells[indx]; + while (iter) { + next = iter->next; + iter->key->release(); + iter->value->release(); + free(iter); + iter = next; + } + } + free(mCells); +} + +void HashMap::allocate(unsigned int size) +{ + HashMapCell ** cells; + unsigned int indx, nindx; + HashMapIter * iter, * next; + + if (mAllocated == size) + return; + + cells = (HashMapCell **) calloc(size, sizeof(HashMapCell *)); + /* iterate over initial hash and copy items in second hash */ + for(indx = 0 ; indx < mAllocated ; indx ++) { + iter = (HashMapIter *) mCells[indx]; + while (iter) { + next = iter->next; + nindx = iter->func % size; + iter->next = cells[nindx]; + cells[nindx] = iter; + iter = next; + } + } + free(mCells); + mAllocated = size; + mCells = (void **) cells; +} + +HashMap * HashMap::hashMap() +{ + HashMap * result = new HashMap(); + return (HashMap *) result->autorelease(); +} + +#if 0 +String * HashMap::className() +{ + return MCSTR("HashMap"); +} +#endif + +String * HashMap::description() +{ + String * result = String::string(); + Array * keys = allKeys(); + result->appendUTF8Characters("{"); + for(unsigned int i = 0 ; i < keys->count() ; i ++) { + Object * key = keys->objectAtIndex(i); + if (i != 0) { + result->appendUTF8Characters(","); + } + result->appendString(key->description()); + result->appendUTF8Characters(":"); + Object * value = objectForKey(key); + result->appendString(value->description()); + } + result->appendUTF8Characters("}"); + + return result; +} + +Object * HashMap::copy() +{ + return new HashMap(this); +} + +unsigned int HashMap::count() +{ + return mCount; +} + +void HashMap::setObjectForKey(Object * key, Object * value) +{ + unsigned int func, indx; + HashMapIter * iter, * cell; + + if (mCount > mAllocated * CHASH_MAXDEPTH) { + allocate((mCount / CHASH_MAXDEPTH) * 2 + 1); + } + + func = key->hash(); + indx = func % mAllocated; + + /* look for the key in existing cells */ + iter = (HashMapIter *) mCells[indx]; + while (iter) { + if (iter->func == func && iter->key->isEqual(key)) { + /* found, replacing entry */ + value->retain(); + iter->value->release(); + iter->value = value; + return; + } + iter = iter->next; + } + + /* not found, adding entry */ + cell = (HashMapCell *) malloc(sizeof(HashMapCell)); + cell->key = key->copy(); + cell->value = value->retain(); + cell->func = func; + cell->next = (HashMapCell *) mCells[indx]; + mCells[indx] = cell; + mCount ++; +} + +void HashMap::removeObjectForKey(Object * key) +{ + unsigned int func, indx; + HashMapIter * iter, * old; + + func = key->hash();; + indx = func % mAllocated; + + /* look for the key in existing cells */ + old = NULL; + iter = (HashMapIter *) mCells[indx]; + while (iter) { + if (iter->func == func && iter->key->isEqual(key)) { + /* found, deleting */ + if (old) + old->next = iter->next; + else + mCells[indx] = iter->next; + iter->key->release(); + iter->value->release(); + free(iter); + mCount --; + return; + } + old = iter; + iter = iter->next; + } + // Not found. +} + +Object * HashMap::objectForKey(Object * key) +{ + unsigned int func; + HashMapIter * iter; + + func = key->hash(); + + /* look for the key in existing cells */ + iter = (HashMapIter *) mCells[func % mAllocated]; + while (iter) { + if (iter->func == func && key->isEqual(iter->key)) { + return iter->value; /* found */ + } + iter = iter->next; + } + return NULL; +} + +HashMapIter * HashMap::iteratorBegin() +{ + HashMapIter * iter; + unsigned int indx = 0; + + iter = (HashMapIter *) mCells[0]; + while (!iter) { + indx ++; + if (indx >= mAllocated) + return NULL; + iter = (HashMapIter *) mCells[indx]; + } + return iter; +} + +HashMapIter * HashMap::iteratorNext(HashMapIter * iter) +{ + unsigned int indx; + + if (!iter) + return NULL; + + indx = iter->func % mAllocated; + iter = iter->next; + + while(!iter) { + indx++; + if (indx >= mAllocated) + return NULL; + iter = (HashMapIter *) mCells[indx]; + } + return iter; +} + +Array * HashMap::allKeys() +{ + Array * keys = Array::array(); + for(HashMapIter * iter = iteratorBegin() ; iter != NULL ; iter = iteratorNext(iter)) { + keys->addObject(iter->key); + } + return keys; +} + +Array * HashMap::allValues() +{ + Array * values = Array::array(); + for(HashMapIter * iter = iteratorBegin() ; iter != NULL ; iter = iteratorNext(iter)) { + values->addObject(iter->value); + } + return values; +} + +void HashMap::removeAllObjects() +{ + for(unsigned int indx = 0 ; indx < mAllocated ; indx++) { + HashMapIter * iter, * next; + iter = (HashMapIter *) mCells[indx]; + while (iter) { + next = iter->next; + iter->key->release(); + iter->value->release(); + free(iter); + iter = next; + } + } + memset(mCells, 0, mAllocated * sizeof(* mCells)); + mCount = 0; +} diff --git a/src/core/basetypes/MCHashMap.h b/src/core/basetypes/MCHashMap.h new file mode 100644 index 00000000..99e8db52 --- /dev/null +++ b/src/core/basetypes/MCHashMap.h @@ -0,0 +1,45 @@ +#ifndef __MAILCORE_MCHASHMAP_H_ + +#define __MAILCORE_MCHASHMAP_H_ + +#include <mailcore/MCObject.h> + +namespace mailcore { + + class String; + class Array; + struct HashMapCell; + typedef HashMapCell HashMapIter; + + class HashMap : public Object { + private: + unsigned int mAllocated; + unsigned int mCount; + void ** mCells; + HashMapIter * iteratorBegin(); + HashMapIter * iteratorNext(HashMapIter * iter); + void allocate(unsigned int size); + void init(); + public: + HashMap(); + HashMap(HashMap * o); + virtual ~HashMap(); + + static HashMap * hashMap(); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + + virtual unsigned int count(); + virtual void setObjectForKey(Object * key, Object * value); + virtual void removeObjectForKey(Object * key); + virtual Object * objectForKey(Object * key); + virtual Array * allKeys(); + virtual Array * allValues(); + virtual void removeAllObjects(); + }; + +} + +#endif diff --git a/src/core/basetypes/MCLog.cc b/src/core/basetypes/MCLog.cc new file mode 100644 index 00000000..2ceceecc --- /dev/null +++ b/src/core/basetypes/MCLog.cc @@ -0,0 +1,30 @@ +#include "MCLog.h" + +#include <stdarg.h> +#include <stdio.h> + +static void logInternalv(FILE * file, + const char * user, const char * filename, unsigned int line, + int dumpStack, const char * format, va_list argp); + +void mailcore::logInternal(const char * user, + const char * filename, + unsigned int line, + int dumpStack, + const char * format, ...) +{ + va_list argp; + + va_start(argp, format); + logInternalv(stderr, user, filename, line, dumpStack, format, argp); + va_end(argp); +} + +static void logInternalv(FILE * file, + const char * user, const char * filename, unsigned int line, + int dumpStack, const char * format, va_list argp) +{ + fprintf(file, "%s:%i: ", filename, line); + vfprintf(file, format, argp); + fprintf(file, "\n"); +} diff --git a/src/core/basetypes/MCLog.h b/src/core/basetypes/MCLog.h new file mode 100644 index 00000000..42246e61 --- /dev/null +++ b/src/core/basetypes/MCLog.h @@ -0,0 +1,19 @@ +#ifndef __MAILCORE_MCLOG_H_ + +#define __MAILCORE_MCLOG_H_ + +#include <stdio.h> + +#define MCLog(...) mailcore::logInternal(NULL, __FILE__, __LINE__, 0, __VA_ARGS__) + +namespace mailcore { + + void logInternal(const char * user, + const char * filename, + unsigned int line, + int dumpStack, + const char * format, ...) __printflike(5, 6); + +} + +#endif diff --git a/src/core/basetypes/MCMainThread.h b/src/core/basetypes/MCMainThread.h new file mode 100644 index 00000000..db8e1141 --- /dev/null +++ b/src/core/basetypes/MCMainThread.h @@ -0,0 +1,11 @@ +#ifndef __MAILCORE_MCMAINTHREAD_H + +#define __MAILCORE_MCMAINTHREAD_H + +namespace mailcore { + void callOnMainThread(void (*)(void *), void * context); + void callOnMainThreadAndWait(void (*)(void *), void * context); + void callAfterDelay(void (*)(void *), void * context, double time); +} + +#endif diff --git a/src/core/basetypes/MCMainThread.mm b/src/core/basetypes/MCMainThread.mm new file mode 100644 index 00000000..31d83b5e --- /dev/null +++ b/src/core/basetypes/MCMainThread.mm @@ -0,0 +1,60 @@ +#include "MCMainThread.h" + +#import <Foundation/Foundation.h> + +using namespace mailcore; + +@interface LEPPPMainThreadCaller : NSObject { + void (* _function)(void *); + void * _context; +} + +@property (nonatomic, assign) void (* function)(void *); +@property (nonatomic, assign) void * context; + +- (void) call; + +@end + +@implementation LEPPPMainThreadCaller + +@synthesize function = _function; +@synthesize context = _context; + +- (void) call +{ + _function(_context); +} + +@end + +void mailcore::callOnMainThread(void (* function)(void *), void * context) +{ + LEPPPMainThreadCaller * caller; + caller = [[LEPPPMainThreadCaller alloc] init]; + [caller setFunction:function]; + [caller setContext:context]; + [caller performSelectorOnMainThread:@selector(call) withObject:nil waitUntilDone:NO]; + [caller release]; +} + +void mailcore::callOnMainThreadAndWait(void (* function)(void *), void * context) +{ + LEPPPMainThreadCaller * caller; + caller = [[LEPPPMainThreadCaller alloc] init]; + [caller setFunction:function]; + [caller setContext:context]; + [caller performSelectorOnMainThread:@selector(call) withObject:nil waitUntilDone:YES]; + [caller release]; +} + +void mailcore::callAfterDelay(void (* function)(void *), void * context, double time) +{ + LEPPPMainThreadCaller * caller; + caller = [[LEPPPMainThreadCaller alloc] init]; + [caller setFunction:function]; + [caller setContext:context]; + [caller performSelector:@selector(call) withObject:nil afterDelay:time]; + [caller release]; +} + diff --git a/src/core/basetypes/MCObject.cc b/src/core/basetypes/MCObject.cc new file mode 100644 index 00000000..220a5a16 --- /dev/null +++ b/src/core/basetypes/MCObject.cc @@ -0,0 +1,180 @@ +#include "MCObject.h" + +#include <stdlib.h> +#include <typeinfo> +#include <cxxabi.h> + +#include "MCAutoreleasePool.h" +#include "MCString.h" +#include "MCHash.h" +#include "MCLog.h" +#include "MCUtils.h" +#include "MCAssert.h" +#include "MCMainThread.h" + +using namespace mailcore; + +Object::Object() +{ + init(); +} + +Object::~Object() +{ +} + +void Object::init() +{ + pthread_mutex_init(&mLock, NULL); + mCounter = 1; +} + +int Object::retainCount() +{ + pthread_mutex_lock(&mLock); + int value = mCounter; + pthread_mutex_unlock(&mLock); + + return value; +} + +Object * Object::retain() +{ + pthread_mutex_lock(&mLock); + mCounter ++; + pthread_mutex_unlock(&mLock); + return this; +} + +void Object::release() +{ + bool shouldRelease = false; + + pthread_mutex_lock(&mLock); + mCounter --; + if (mCounter == 0) { + shouldRelease = true; + } + pthread_mutex_unlock(&mLock); + + if (shouldRelease) { + //MCLog("dealloc %s", className()->description()->UTF8Characters()); + delete this; + } +} + +Object * Object::autorelease() +{ + AutoreleasePool::autorelease(this); + return this; +} + +String * Object::className() +{ + int status; + char * unmangled = abi::__cxa_demangle(typeid(* this).name(), NULL, NULL, &status); + //return mailcore::String::uniquedStringWithUTF8Characters(typeid(* this).name()); + return mailcore::String::uniquedStringWithUTF8Characters(unmangled); +} + +/* +String * Object::className() +{ + return MCSTR("Object"); +} +*/ + +String * Object::description() +{ + return String::stringWithUTF8Format("<%s:%p>", className()->UTF8Characters(), this); +} + +bool Object::isEqual(Object * otherObject) +{ + return this == otherObject; +} + +unsigned int Object::hash() +{ + return hashCompute((const char *) this, sizeof(this)); +} + +Object * Object::copy() +{ + MCAssert(0); + return NULL; +} + +void Object::performMethod(Object::Method method, void * context) +{ + (this->*method)(context); +} + +struct mainThreadCallData { + Object * obj; + void * context; + Object::Method method; +}; + +static void performOnMainThread(void * info) +{ + struct mainThreadCallData * data; + void * context; + Object * obj; + Object::Method method; + + data = (struct mainThreadCallData *) info; + obj = data->obj; + context = data->context; + method = data->method; + + (obj->*method)(context); + + free(data); +} + +static void callAfterDelay(void * info) +{ + struct mainThreadCallData * data; + void * context; + Object * obj; + Object::Method method; + + data = (struct mainThreadCallData *) info; + obj = data->obj; + context = data->context; + method = data->method; + + (obj->*method)(context); + + free(data); +} + +void Object::performMethodOnMainThread(Method method, void * context, bool waitUntilDone) +{ + struct mainThreadCallData * data; + + data = (struct mainThreadCallData *) calloc(sizeof(* data), 1); + data->obj = this; + data->context = context; + data->method = method; + + if (waitUntilDone) { + callOnMainThreadAndWait(performOnMainThread, data); + } + else { + callOnMainThread(performOnMainThread, data); + } +} + +void Object::performMethodAfterDelay(Method method, void * context, double delay) +{ + struct mainThreadCallData * data; + + data = (struct mainThreadCallData *) calloc(sizeof(* data), 1); + data->obj = this; + data->context = context; + data->method = method; + + callAfterDelay(performOnMainThread, data, delay); +} diff --git a/src/core/basetypes/MCObject.h b/src/core/basetypes/MCObject.h new file mode 100644 index 00000000..9e323ebf --- /dev/null +++ b/src/core/basetypes/MCObject.h @@ -0,0 +1,41 @@ +#ifndef __MAILCORE_MCOBJECT_H_ + +#define __MAILCORE_MCOBJECT_H_ + +#include <pthread.h> + +namespace mailcore { + + class String; + + class Object { + private: + pthread_mutex_t mLock; + int mCounter; + void init(); + public: + Object(); + virtual ~Object(); + + virtual int retainCount(); + virtual Object * retain(); + virtual void release(); + virtual Object * autorelease(); + virtual String * description(); + virtual String * className(); + + virtual bool isEqual(Object * otherObject); + virtual unsigned int hash(); + + // optional + virtual Object * copy(); + + typedef void (Object::*Method) (void *); + virtual void performMethod(Method method, void * context); + virtual void performMethodOnMainThread(Method method, void * context, bool waitUntilDone = false); + virtual void performMethodAfterDelay(Method method, void * context, double delay); + }; + +} + +#endif diff --git a/src/core/basetypes/MCOperation.cc b/src/core/basetypes/MCOperation.cc new file mode 100644 index 00000000..61dd48e6 --- /dev/null +++ b/src/core/basetypes/MCOperation.cc @@ -0,0 +1,45 @@ +#include "MCOperation.h" + +using namespace mailcore; + +Operation::Operation() +{ + mCallback = NULL; + mCancelled = false; + pthread_mutex_init(&mLock, NULL); +} + +Operation::~Operation() +{ + pthread_mutex_destroy(&mLock); +} + +void Operation::setCallback(OperationCallback * callback) +{ + mCallback = callback; +} + +OperationCallback * Operation::callback() +{ + return mCallback; +} + +void Operation::cancel() +{ + pthread_mutex_lock(&mLock); + mCancelled = true; + pthread_mutex_unlock(&mLock); +} + +bool Operation::isCancelled() +{ + pthread_mutex_lock(&mLock); + bool value = mCancelled; + pthread_mutex_unlock(&mLock); + + return value; +} + +void Operation::main() +{ +} diff --git a/src/core/basetypes/MCOperation.h b/src/core/basetypes/MCOperation.h new file mode 100644 index 00000000..80bd8d30 --- /dev/null +++ b/src/core/basetypes/MCOperation.h @@ -0,0 +1,33 @@ +#ifndef __MAILCORE_MCOPERATION_H_ + +#define __MAILCORE_MCOPERATION_H_ + +#include <pthread.h> +#include <mailcore/MCObject.h> + +namespace mailcore { + + class OperationCallback; + + class Operation : public Object { + private: + OperationCallback * mCallback; + bool mCancelled; + pthread_mutex_t mLock; + + public: + Operation(); + virtual ~Operation(); + + virtual void setCallback(OperationCallback * callback); + virtual OperationCallback * callback(); + + virtual void cancel(); + virtual bool isCancelled(); + + virtual void main(); + }; + +} + +#endif diff --git a/src/core/basetypes/MCOperationCallback.h b/src/core/basetypes/MCOperationCallback.h new file mode 100644 index 00000000..e9623c76 --- /dev/null +++ b/src/core/basetypes/MCOperationCallback.h @@ -0,0 +1,16 @@ +#ifndef __MAILCORE_MCOPERATIONCALLBACK_H_ + +#define __MAILCORE_MCOPERATIONCALLBACK_H_ + +namespace mailcore { + + class Operation; + + class OperationCallback { + public: + virtual void operationFinished(Operation * op) {} + }; + +} + +#endif diff --git a/src/core/basetypes/MCOperationQueue.cc b/src/core/basetypes/MCOperationQueue.cc new file mode 100644 index 00000000..3c22b89c --- /dev/null +++ b/src/core/basetypes/MCOperationQueue.cc @@ -0,0 +1,179 @@ +#include "MCOperationQueue.h" + +#include "MCOperation.h" +#include "MCOperationCallback.h" +#include "MCMainThread.h" +#include "MCUtils.h" +#include "MCArray.h" +#include "MCLog.h" +#include <libetpan/libetpan.h> + +using namespace mailcore; + +OperationQueue::OperationQueue() +{ + mOperations = new Array(); + mStarted = false; + //sem_init(&mOperationSem, 0, 0); + //sem_init(&mStartSem, 0, 0); + //sem_init(&mStopSem, 0, 0); + pthread_mutex_init(&mLock, NULL); + mWaiting = false; + //sem_init(&mWaitingFinishedSem, 0, 0); + mOperationSem = mailsem_new(); + mStartSem = mailsem_new(); + mStopSem = mailsem_new(); + mWaitingFinishedSem = mailsem_new(); +} + +OperationQueue::~OperationQueue() +{ + MC_SAFE_RELEASE(mOperations); + //sem_destroy(&mOperationSem); + //sem_destroy(&mStartSem); + //sem_destroy(&mStopSem); + pthread_mutex_destroy(&mLock); + //sem_destroy(&mWaitingFinishedSem); + mailsem_free(mOperationSem); + mailsem_free(mStartSem); + mailsem_free(mStopSem); + mailsem_free(mWaitingFinishedSem); +} + +void OperationQueue::addOperation(Operation * op) +{ + pthread_mutex_lock(&mLock); + mOperations->addObject(op); + pthread_mutex_unlock(&mLock); + //sem_post(&mOperationSem); + mailsem_up(mOperationSem); + startThread(); +} + +void OperationQueue::runOperationsOnThread(OperationQueue * queue) +{ + queue->runOperations(); +} + +void OperationQueue::runOperations() +{ + MCLog("start thread"); + //sem_post(&mStartSem); + mailsem_up(mStartSem); + + while (true) { + Operation * op = NULL; + bool needsCheckRunning = false; + + //int value = 0; + //int r; + + //r = sem_getvalue(&mOperationSem, &value); + //MCLog("x before sem %i %i", value, r); + //sem_wait(&mOperationSem); + mailsem_down(mOperationSem); + //sem_getvalue(&mOperationSem, &value); + //MCLog("x after sem %i", value); + + pthread_mutex_lock(&mLock); + if (mOperations->count() > 0) { + op = (Operation *) mOperations->objectAtIndex(0); + } + pthread_mutex_unlock(&mLock); + + if (op == NULL) { + //sem_post(&mStopSem); + mailsem_up(mStopSem); + break; + } + + op->main(); + + if (op->callback() != NULL) { + performMethodOnMainThread((Object::Method) &OperationQueue::callbackOnMainThread, op, true); + } + + pthread_mutex_lock(&mLock); + mOperations->removeObjectAtIndex(0); + if (mOperations->count() == 0) { + if (mWaiting) { + //sem_post(&mWaitingFinishedSem); + mailsem_up(mWaitingFinishedSem); + } + needsCheckRunning = true; + } + pthread_mutex_unlock(&mLock); + + if (needsCheckRunning) { + retain(); // (1) + performMethodOnMainThread((Object::Method) &OperationQueue::checkRunningOnMainThread, this); + } + } + MCLog("cleanup thread"); +} + +void OperationQueue::callbackOnMainThread(Operation * op) +{ + if (op->callback() != NULL) { + op->callback()->operationFinished(op); + } +} + +void OperationQueue::checkRunningOnMainThread(void * context) +{ + performMethodAfterDelay((Object::Method) &OperationQueue::checkRunningAfterDelay, NULL, 1); +} + +void OperationQueue::checkRunningAfterDelay(void * context) +{ + bool quitting = false; + + pthread_mutex_lock(&mLock); + if (mOperations->count() == 0) { + //sem_post(&mOperationSem); + mailsem_up(mOperationSem); + quitting = true; + } + pthread_mutex_unlock(&mLock); + + // Number of operations can't be changed because it runs on main thread. + // And addOperation() should also be called from main thread. + + if (quitting) { + //sem_wait(&mStopSem); + mailsem_down(mStopSem); + mStarted = false; + } + + release(); // (1) +} + +void OperationQueue::startThread() +{ + if (mStarted) + return; + + mStarted = true; + pthread_create(&mThreadID, NULL, (void * (*)(void *)) OperationQueue::runOperationsOnThread, this); + //sem_wait(&mStartSem); + mailsem_down(mStartSem); +} + +#if 0 +void OperationQueue::waitUntilAllOperationsAreFinished() +{ + bool waiting = false; + + pthread_mutex_lock(&mLock); + if (mOperations->count() > 0) { + mWaiting = true; + waiting = true; + } + pthread_mutex_unlock(&mLock); + + if (waiting) { + sem_wait(&mWaitingFinishedSem); + } + mWaiting = false; +} +#endif diff --git a/src/core/basetypes/MCOperationQueue.h b/src/core/basetypes/MCOperationQueue.h new file mode 100644 index 00000000..b858aebc --- /dev/null +++ b/src/core/basetypes/MCOperationQueue.h @@ -0,0 +1,45 @@ +#ifndef __MAILCORE_MCOPERATIONQUEUE_H_ + +#define __MAILCORE_MCOPERATIONQUEUE_H_ + +#include <pthread.h> +#include <semaphore.h> +#include <mailcore/MCObject.h> +#include <libetpan/libetpan.h> + +namespace mailcore { + + class Operation; + class Array; + + class OperationQueue : public Object { + private: + Array * mOperations; + pthread_t mThreadID; + bool mStarted; + struct mailsem * mOperationSem; + struct mailsem * mStartSem; + struct mailsem * mStopSem; + pthread_mutex_t mLock; + bool mWaiting; + struct mailsem * mWaitingFinishedSem; + + void startThread(); + static void runOperationsOnThread(OperationQueue * queue); + void runOperations(); + void callbackOnMainThread(Operation * op); + void checkRunningOnMainThread(void * context); + void checkRunningAfterDelay(void * context); + + public: + OperationQueue(); + virtual ~OperationQueue(); + + virtual void addOperation(Operation * op); + + //virtual void waitUntilAllOperationsAreFinished(); + }; + +} + +#endif diff --git a/src/core/basetypes/MCRange.cc b/src/core/basetypes/MCRange.cc new file mode 100644 index 00000000..ead5c189 --- /dev/null +++ b/src/core/basetypes/MCRange.cc @@ -0,0 +1,12 @@ +#include "MCRange.h" + +using namespace mailcore; + +Range mailcore::RangeMake(unsigned int index, unsigned int length) +{ + Range range; + range.index = index; + range.length = length; + return range; +} + diff --git a/src/core/basetypes/MCRange.h b/src/core/basetypes/MCRange.h new file mode 100644 index 00000000..752c4bec --- /dev/null +++ b/src/core/basetypes/MCRange.h @@ -0,0 +1,15 @@ +#ifndef __MAILCORE_MCRANGE_H_ + +#define __MAILCORE_MCRANGE_H_ + +namespace mailcore { + + struct Range { + unsigned int index; + unsigned int length; + }; + + Range RangeMake(unsigned int index, unsigned int length); +} + +#endif diff --git a/src/core/basetypes/MCSet.cc b/src/core/basetypes/MCSet.cc new file mode 100644 index 00000000..fcda7f62 --- /dev/null +++ b/src/core/basetypes/MCSet.cc @@ -0,0 +1,106 @@ +#include "MCSet.h" + +#include "MCHashMap.h" +#include "MCString.h" +#include "MCUtils.h" +#include "MCArray.h" +#include "MCLog.h" + +using namespace mailcore; + +void Set::init() +{ + mHash = new HashMap(); +} + +Set::Set() +{ + init(); +} + +Set::Set(Set * o) +{ + init(); + MC_SAFE_REPLACE_COPY(HashMap, mHash, o->mHash); +} + +Set::~Set() +{ + delete mHash; +} + +Set * Set::set() +{ + Set * result = new Set(); + return (Set *) result->autorelease(); +} + +Set * Set::setWithArray(Array * objects) +{ + Set * result = new Set(); + result->addObjectsFromArray(objects); + return (Set *) result->autorelease(); +} + +#if 0 +String * Set::className() +{ + return MCSTR("Set"); +} +#endif + +String * Set::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p ", className(), this); + result->appendString(mHash->allKeys()->description()); + result->appendUTF8Characters(">"); + return result; +} + +Object * Set::copy() +{ + return new Set(this); +} + +unsigned int Set::count() +{ + return mHash->count(); +} + +void Set::addObject(Object * obj) +{ + mHash->setObjectForKey(obj, obj); +} + +void Set::removeObject(Object * obj) +{ + mHash->removeObjectForKey(obj); +} + +Object * Set::member(Object * obj) +{ + return mHash->objectForKey(obj); +} + +bool Set::containsObject(Object * obj) +{ + return (mHash->objectForKey(obj) != NULL); +} + +Array * Set::allObjects() +{ + return mHash->allKeys(); +} + +void Set::removeAllObjects() +{ + mHash->removeAllObjects(); +} + +void Set::addObjectsFromArray(Array * objects) +{ + for(unsigned int i= 0 ; i < objects->count() ; i ++) { + addObject(objects->objectAtIndex(i)); + } +} diff --git a/src/core/basetypes/MCSet.h b/src/core/basetypes/MCSet.h new file mode 100644 index 00000000..0f1ab360 --- /dev/null +++ b/src/core/basetypes/MCSet.h @@ -0,0 +1,42 @@ +#ifndef __MAILCORE_CSET_H_ + +#define __MAILCORE_CSET_H_ + +#include <mailcore/MCObject.h> + +namespace mailcore { + + class String; + class Array; + class HashMap; + + class Set : public Object { + private: + HashMap * mHash; + void init(); + public: + Set(); + Set(Set * o); + virtual ~Set(); + + static Set * set(); + static Set * setWithArray(Array * objects); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + + virtual unsigned int count(); + virtual void addObject(Object * obj); + virtual void removeObject(Object * obj); + virtual bool containsObject(Object * obj); + virtual Object * member(Object * obj); + + virtual Array * allObjects(); + virtual void removeAllObjects(); + virtual void addObjectsFromArray(Array * objects); + }; + +} + +#endif diff --git a/src/core/basetypes/MCString.cc b/src/core/basetypes/MCString.cc new file mode 100644 index 00000000..5f6948d6 --- /dev/null +++ b/src/core/basetypes/MCString.cc @@ -0,0 +1,1958 @@ +#include "MCString.h" + +#include <string.h> +#include <stdlib.h> +#include <unicode/ustring.h> +#include <unicode/ucnv.h> +#include <uuid/uuid.h> +#include <pthread.h> +#include <libetpan/libetpan.h> +#include <libxml/xmlmemory.h> +#include <libxml/HTMLparser.h> + +#include "MCData.h" +#include "MCHash.h" +#include "MCLog.h" +#include "MCUtils.h" +#include "MCRange.h" +#include "MCArray.h" +#include "MCSet.h" +#include "MCHashMap.h" +#include "MCAutoreleasePool.h" +#include "MCValue.h" + +using namespace mailcore; + +#pragma mark quote headers string + +static inline int to_be_quoted(const char * word, size_t size, int subject) +{ + int do_quote; + const char * cur; + size_t i; + + do_quote = 0; + cur = word; + for(i = 0 ; i < size ; i ++) { + if (* cur == '=') + do_quote = 1; + + if (!subject) { + switch (* cur) { + case ',': + case ':': + case '!': + case '"': + case '#': + case '$': + case '@': + case '[': + case '\\': + case ']': + case '^': + case '`': + case '{': + case '|': + case '}': + case '~': + case '=': + case '?': + case '_': + do_quote = 1; + break; + } + } + if (((unsigned char) * cur) >= 128) + do_quote = 1; + + cur ++; + } + + return do_quote; +} + +#define MAX_IMF_LINE 72 + +static inline void quote_word(const char * display_charset, + MMAPString * mmapstr, const char * word, size_t size) +{ + const char * cur; + size_t i; + char hex[4]; + int col; + + mmap_string_append(mmapstr, "=?"); + mmap_string_append(mmapstr, display_charset); + mmap_string_append(mmapstr, "?Q?"); + + col = (int) mmapstr->len; + + cur = word; + for(i = 0 ; i < size ; i ++) { + int do_quote_char; + + do_quote_char = 0; + switch (* cur) { + case ',': + case ':': + case '!': + case '"': + case '#': + case '$': + case '@': + case '[': + case '\\': + case ']': + case '^': + case '`': + case '{': + case '|': + case '}': + case '~': + case '=': + case '?': + case '_': + do_quote_char = 1; + break; + + default: + if (((unsigned char) * cur) >= 128) + do_quote_char = 1; + break; + } + + if (do_quote_char) { + snprintf(hex, 4, "=%2.2X", (unsigned char) * cur); + mmap_string_append(mmapstr, hex); + col += 3; + } + else { + if (* cur == ' ') { + mmap_string_append_c(mmapstr, '_'); + } + else { + mmap_string_append_c(mmapstr, * cur); + } + col += 3; + } + cur ++; + } + + mmap_string_append(mmapstr, "?="); +} + +static inline void get_word(const char * begin, const char ** pend, int subject, int * pto_be_quoted) +{ + const char * cur; + + cur = begin; + + while ((* cur != ' ') && (* cur != '\t') && (* cur != '\0')) { + cur ++; + } + while (((* cur == ' ') || (* cur == '\t')) && (* cur != '\0')) { + cur ++; + } + + if (cur - begin + + 1 /* minimum column of string in a + folded header */ > MAX_IMF_LINE) + * pto_be_quoted = 1; + else + * pto_be_quoted = to_be_quoted(begin, cur - begin, subject); + + * pend = cur; +} + +static char * etpan_make_full_quoted_printable(const char * display_charset, + const char * phrase) +{ + int needs_quote; + char * str; + + needs_quote = to_be_quoted(phrase, strlen(phrase), 0); + if (needs_quote) { + MMAPString * mmapstr; + + mmapstr = mmap_string_new(""); + quote_word(display_charset, mmapstr, phrase, strlen(phrase)); + str = strdup(mmapstr->str); + mmap_string_free(mmapstr); + } + else { + str = strdup(phrase); + } + + return str; +} + +static char * etpan_make_quoted_printable(const char * display_charset, + const char * phrase, int subject) +{ + char * str; + const char * cur; + MMAPString * mmapstr; + + mmapstr = mmap_string_new(""); + + cur = phrase; + while (* cur != '\0') { + const char * begin; + const char * end; + int do_quote; + int quote_words; + + begin = cur; + end = begin; + quote_words = 0; + do_quote = 1; + + while (* cur != '\0') { + get_word(cur, &cur, subject, &do_quote); + if (do_quote) { + quote_words = 1; + end = cur; + } + else + break; + if (* cur != '\0') + cur ++; + } + + if (quote_words) { + quote_word(display_charset, mmapstr, begin, end - begin); + + if ((* end == ' ') || (* end == '\t')) { + mmap_string_append_c(mmapstr, * end); + end ++; + } + + if (* end != '\0') { + mmap_string_append_len(mmapstr, end, cur - end); + } + } + else { + mmap_string_append_len(mmapstr, begin, cur - begin); + } + + if ((* cur == ' ') || (* cur == '\t')) { + mmap_string_append_c(mmapstr, * cur); + cur ++; + } + } + + str = strdup(mmapstr->str); + mmap_string_free(mmapstr); + + return str; +} + +#pragma mark extract subject + +static inline int skip_subj_blob(char * subj, size_t * begin, + size_t length, int keep_bracket) +{ + if (keep_bracket) + return 0; + + /* subj-blob = "[" *BLOBCHAR "]" *WSP */ + size_t cur_token; + + cur_token = * begin; + + if (subj[cur_token] != '[') + return 0; + + cur_token ++; + + while (1) { + if (cur_token >= length) + return 0; + + if (subj[cur_token] == '[') + return 0; + + if (subj[cur_token] == ']') + break; + + cur_token ++; + } + + cur_token ++; + + while (1) { + if (cur_token >= length) + break; + + if (subj[cur_token] != ' ') + break; + + cur_token ++; + } + + * begin = cur_token; + + return 1; +} + +static inline int skip_subj_refwd(char * subj, size_t * begin, + size_t length, int keep_bracket) +{ + /* subj-refwd = ("re" / ("fw" ["d"])) *WSP [subj-blob] ":" */ + size_t cur_token; + int prefix; + int has_suffix; + + cur_token = * begin; + prefix = 0; + if (!prefix) { + if (length - cur_token >= 18) { + if (strncasecmp(subj + cur_token, "Переслать", 18) == 0) { + cur_token += 18; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 10) { + if (strncasecmp(subj + cur_token, "Ответ", 10) == 0) { + cur_token += 10; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 7) { + if (strncasecmp(subj + cur_token, "Antwort", 7) == 0) { + cur_token += 7; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 6) { + if (strncasecmp(subj + cur_token, "回复", 6) == 0) { + cur_token += 6; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "转发", 6) == 0) { + cur_token += 6; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 5) { + // é is 2 chars in utf-8 + if (strncasecmp(subj + cur_token, "réf.", 5) == 0) { + cur_token += 5; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "rép.", 5) == 0) { + cur_token += 5; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "trans", 5) == 0) { + cur_token += 5; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 4) { + if (strncasecmp(subj + cur_token, "antw", 4) == 0) { + cur_token += 4; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 3) { + if (strncasecmp(subj + cur_token, "fwd", 3) == 0) { + cur_token += 3; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "ogg", 3) == 0) { + cur_token += 3; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "odp", 3) == 0) { + cur_token += 3; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "res", 3) == 0) { + cur_token += 3; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "end", 3) == 0) { + cur_token += 3; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 2) { + if (strncasecmp(subj + cur_token, "fw", 2) == 0) { + cur_token += 2; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "re", 2) == 0) { + cur_token += 2; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "tr", 2) == 0) { + cur_token += 2; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "aw", 2) == 0) { + cur_token += 2; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "sv", 2) == 0) { + cur_token += 2; + prefix = 1; + } + else if (strncasecmp(subj + cur_token, "rv", 2) == 0) { + cur_token += 2; + prefix = 1; + } + } + } + if (!prefix) { + if (length - cur_token >= 1) { + if (strncasecmp(subj + cur_token, "r", 1) == 0) { + cur_token += 1; + prefix = 1; + } + } + } + + if (!prefix) + return 0; + + while (1) { + if (cur_token >= length) + break; + + if (subj[cur_token] != ' ') + break; + + cur_token ++; + } + + skip_subj_blob(subj, &cur_token, length, keep_bracket); + + has_suffix = 0; + + if (!has_suffix) { + if (length - cur_token >= 3) { + if (strncasecmp(subj + cur_token, ":", 3) == 0) { + cur_token += 3; + has_suffix = 1; + } + } + } + + if (!has_suffix) { + if (cur_token < length) { + if (subj[cur_token] == ':') { + cur_token ++; + has_suffix = 1; + } + } + } + + if (!has_suffix) { + return 0; + } + + * begin = cur_token; + + return 1; +} + +static inline int skip_subj_leader(char * subj, size_t * begin, + size_t length, int keep_bracket) +{ + size_t cur_token; + + cur_token = * begin; + + /* subj-leader = (*subj-blob subj-refwd) / WSP */ + + if (subj[cur_token] == ' ') { + cur_token ++; + } + else { + while (cur_token < length) { + if (!skip_subj_blob(subj, &cur_token, length, keep_bracket)) + break; + } + if (!skip_subj_refwd(subj, &cur_token, length, keep_bracket)) + return 0; + } + + * begin = cur_token; + + return 1; +} + +static char * extract_subject(char * str, int keep_bracket) +{ + char * subj; + char * cur; + char * write_pos; + size_t len; + size_t begin; + int do_repeat_5; + int do_repeat_6; + + /* + (1) Convert any RFC 2047 encoded-words in the subject to + UTF-8. + We work on UTF-8 string -- DVH + */ + + subj = strdup(str); + if (subj == NULL) + return NULL; + + len = strlen(subj); + + /* + Convert all tabs and continuations to space. + Convert all multiple spaces to a single space. + */ + + cur = subj; + write_pos = subj; + while (* cur != '\0') { + int cont; + + switch (* cur) { + case '\t': + case '\r': + case '\n': + cont = 1; + + cur ++; + while (* cur && cont) { + switch (* cur) { + case '\t': + case '\r': + case '\n': + cont = 1; + break; + default: + cont = 0; + break; + } + cur ++; + } + + * write_pos = ' '; + write_pos ++; + + break; + + default: + * write_pos = * cur; + write_pos ++; + + cur ++; + + break; + } + } + * write_pos = '\0'; + + begin = 0; + + do { + do_repeat_6 = 0; + + /* + (2) Remove all trailing text of the subject that matches + the subj-trailer ABNF, repeat until no more matches are + possible. + */ + + while (len > 0) { + int chg; + + chg = 0; + + /* subj-trailer = "(fwd)" / WSP */ + if (subj[len - 1] == ' ') { + subj[len - 1] = '\0'; + len --; + } + else { + if (len < 5) + break; + + if (strncasecmp(subj + len - 5, "(fwd)", 5) != 0) + break; + + subj[len - 5] = '\0'; + len -= 5; + } + } + + do { + size_t saved_begin; + + do_repeat_5 = 0; + + /* + (3) Remove all prefix text of the subject that matches the + subj-leader ABNF. + */ + + if (skip_subj_leader(subj, &begin, len, keep_bracket)) + do_repeat_5 = 1; + + /* + (4) If there is prefix text of the subject that matches the + subj-blob ABNF, and removing that prefix leaves a non-empty + subj-base, then remove the prefix text. + */ + + saved_begin = begin; + if (skip_subj_blob(subj, &begin, len, keep_bracket)) { + if (begin == len) { + /* this will leave a empty subject base */ + begin = saved_begin; + } + else + do_repeat_5 = 1; + } + + /* + (5) Repeat (3) and (4) until no matches remain. + Note: it is possible to defer step (2) until step (6), + but this requires checking for subj-trailer in step (4). + */ + + } + while (do_repeat_5); + + /* + (6) If the resulting text begins with the subj-fwd-hdr ABNF + and ends with the subj-fwd-trl ABNF, remove the + subj-fwd-hdr and subj-fwd-trl and repeat from step (2). + */ + + if (len >= 5) { + size_t saved_begin; + + saved_begin = begin; + if (strncasecmp(subj + begin, "[fwd:", 5) == 0) { + begin += 5; + + if (subj[len - 1] != ']') + saved_begin = begin; + else { + subj[len - 1] = '\0'; + len --; + do_repeat_6 = 1; + } + } + } + + } + while (do_repeat_6); + + /* + (7) The resulting text is the "base subject" used in + threading. + */ + + /* convert to upper case */ + + cur = subj + begin; + write_pos = subj; + + while (* cur != '\0') { + * write_pos = * cur; + cur ++; + write_pos ++; + } + * write_pos = '\0'; + + return subj; +} + +String::String(const UChar * unicodeChars) +{ + mUnicodeChars = NULL; + reset(); + appendCharacters(unicodeChars); +} + +String::String(const UChar * unicodeChars, unsigned int length) +{ + mUnicodeChars = NULL; + reset(); + appendCharactersLength(unicodeChars, length); +} + +String::String(const char * UTF8Characters) +{ + mUnicodeChars = NULL; + reset(); + appendUTF8Characters(UTF8Characters); +} + +String::String(String * otherString) +{ + mUnicodeChars = NULL; + reset(); + appendString(otherString); +} + +String::String(Data * data, const char * charset) +{ + mUnicodeChars = NULL; + reset(); + appendBytes(data->bytes(), data->length(), charset); +} + +String::String(const char * bytes, unsigned int length, const char * charset) +{ + mUnicodeChars = NULL; + reset(); + if (charset == NULL) { + appendUTF8CharactersLength(bytes, length); + } + else { + appendBytes(bytes, length, charset); + } +} + +String::~String() +{ + reset(); +} + +void String::allocate(unsigned int length) +{ + length ++; + if (length < mAllocated) + return; + + if (mAllocated == 0) { + mAllocated = 4; + } + while (length > mAllocated) { + mAllocated *= 2; + } + + mUnicodeChars = (UChar *) realloc(mUnicodeChars, mAllocated * sizeof(* mUnicodeChars)); +} + +String * String::string() +{ + return stringWithCharacters(NULL); +} + +String * String::stringWithUTF8Format(const char * format, ...) +{ + va_list argp; + + va_start(argp, format); + String * result = stringWithVUTF8Format(format, argp); + va_end(argp); + + return result; +} + +String * String::stringWithVUTF8Format(const char * format, va_list ap) +{ + char * result; + vasprintf(&result, format, ap); + return stringWithUTF8Characters(result); +} + +String * String::stringWithUTF8Characters(const char * UTF8Characters) +{ + String * result = new String(UTF8Characters); + return (String *) result->autorelease(); +} + +String * String::stringWithCharacters(const UChar * characters) +{ + String * result = new String(characters); + return (String *) result->autorelease(); +} + +String * String::stringWithCharacters(const UChar * characters, unsigned int length) +{ + String * result = new String(characters, length); + return (String *) result->autorelease(); +} + +void String::appendCharactersLength(const UChar * unicodeCharacters, unsigned int length) +{ + allocate(mLength + length); + u_strncpy(&mUnicodeChars[mLength], unicodeCharacters, length); + mLength += length; + mUnicodeChars[mLength] = 0; +} + +void String::appendString(String * otherString) +{ + appendCharactersLength(otherString->unicodeCharacters(), otherString->length()); +} + +void String::appendUTF8Format(const char * format, ...) +{ + va_list argp; + + va_start(argp, format); + String * otherString = stringWithVUTF8Format(format, argp); + va_end(argp); + + this->appendString(otherString); +} + +void String::appendUTF8CharactersLength(const char * UTF8Characters, unsigned int length) +{ + if (UTF8Characters == NULL) + return; + + UChar * dest; + int32_t destLength; + int32_t destCapacity; + UErrorCode err; + + err = U_ZERO_ERROR; + u_strFromUTF8WithSub(NULL, 0, &destLength, UTF8Characters, length, 0xFFFD, NULL, &err); + destCapacity = destLength + 1; + dest = (UChar *) malloc(destCapacity * sizeof(* dest)); + err = U_ZERO_ERROR; + u_strFromUTF8WithSub(dest, destCapacity, &destLength, UTF8Characters, length, 0xFFFD, NULL, &err); + dest[destLength] = 0; + // Fix in case of bad conversion. + for(int32_t i = 0 ; i < destLength ; i ++) { + if (dest[i] == 0) { + dest[i] = ' '; + } + } + + appendCharactersLength(dest, destLength); + + free(dest); +} + +void String::appendUTF8Characters(const char * UTF8Characters) +{ + appendUTF8CharactersLength(UTF8Characters, (unsigned int) strlen(UTF8Characters)); +} + +void String::appendCharacters(const UChar * unicodeCharacters) +{ + if (unicodeCharacters == NULL) + return; + + appendCharactersLength(unicodeCharacters, u_strlen(unicodeCharacters)); +} + +const UChar * String::unicodeCharacters() +{ + return mUnicodeChars; +} + +const char * String::UTF8Characters() +{ + char * dest; + int32_t destLength; + int32_t destCapacity; + UErrorCode err; + + err = U_ZERO_ERROR; + u_strToUTF8(NULL, 0, &destLength, mUnicodeChars, mLength, &err); + destCapacity = destLength + 1; + dest = (char *) malloc(destCapacity * sizeof(* dest)); + err = U_ZERO_ERROR; + u_strToUTF8(dest, destCapacity, &destLength, mUnicodeChars, mLength, &err); + dest[destLength] = 0; + + Data * data = Data::dataWithBytes(dest, destLength + 1); + free(dest); + + return data->bytes(); +} + +unsigned int String::length() +{ + return mLength; +} + +String * String::stringByAppendingString(String * otherString) +{ + String * result = new String(this); + result->appendString(otherString); + return (String *) result->autorelease(); +} + +String * String::stringByAppendingUTF8Format(const char * format, ...) +{ + va_list argp; + + va_start(argp, format); + String * otherString = stringWithVUTF8Format(format, argp); + va_end(argp); + + return this->stringByAppendingString(otherString); +} + +String * String::stringByAppendingUTF8Characters(const char * UTF8Characters) +{ + String * otherString = stringWithUTF8Characters(UTF8Characters); + return this->stringByAppendingString(otherString); +} + +String * String::stringByAppendingCharacters(const UChar * unicodeCharacters) +{ + String * otherString = stringWithCharacters(unicodeCharacters); + return this->stringByAppendingString(otherString); +} + +void String::reset() +{ + free(mUnicodeChars); + mUnicodeChars = NULL; + mLength = 0; + mAllocated = 0; +} + +void String::setString(String * otherString) +{ + reset(); + appendString(otherString); +} + +void String::setUTF8Characters(const char * UTF8Characters) +{ + reset(); + appendUTF8Characters(UTF8Characters); +} + +void String::setCharacters(const UChar * unicodeCharacters) +{ + reset(); + appendCharacters(unicodeCharacters); +} + +#if 0 +String * String::className() +{ + return MCSTR("String"); +} +#endif + +String * String::description() +{ + return this; +} + +Object * String::copy() +{ + return new String(this); +} + +bool String::isEqual(Object * otherObject) +{ + String * otherString = (String *) otherObject; + if (length() != otherString->length()) + return false; + return compare(otherString) == 0; +} + +unsigned int String::hash() +{ + return hashCompute((const char *) mUnicodeChars, mLength * sizeof(* mUnicodeChars)); +} + +#define DEFAULT_INCOMING_CHARSET "iso-8859-1" +#define DEFAULT_DISPLAY_CHARSET "utf-8" + +String * String::stringByDecodingMIMEHeaderValue(const char * phrase) +{ + size_t cur_token; + char * decoded; + String * result; + bool hasEncoding; + + if (phrase == NULL) + return string(); + + if (* phrase == '\0') { + return string(); + } + + hasEncoding = false; + if (strstr(phrase, "=?") != NULL) { + if ((strcasestr(phrase, "?Q?") != NULL) || (strcasestr(phrase, "?B?") != NULL)) { + hasEncoding = true; + } + } + + if (!hasEncoding) { + return Data::dataWithBytes(phrase, (unsigned int) strlen(phrase))->stringWithDetectedCharset(); + } + + cur_token = 0; + decoded = NULL; + mailmime_encoded_phrase_parse(DEFAULT_INCOMING_CHARSET, + phrase, strlen(phrase), + &cur_token, DEFAULT_DISPLAY_CHARSET, + &decoded); + + result = NULL; + if (decoded != NULL) { + result = stringWithUTF8Characters(decoded); + } + else { + MCLog("could not decode: %s\n", phrase); + } + + free(decoded); + + return result; +} + +Data * String::encodedAddressDisplayNameValue() +{ + char * str; + Data * result; + + str = etpan_make_full_quoted_printable(DEFAULT_DISPLAY_CHARSET, UTF8Characters()); + result = Data::dataWithBytes(str, (unsigned int) strlen(str) + 1); + free(str); + + return result; +} + +Data * String::encodedMIMEHeaderValue() +{ + char * str; + Data * result; + + str = etpan_make_quoted_printable(DEFAULT_DISPLAY_CHARSET, UTF8Characters(), 0); + result = Data::dataWithBytes(str, (unsigned int) strlen(str) + 1); + free(str); + + return result; +} + +Data * String::encodedMIMEHeaderValueForSubject() +{ + char * str; + Data * result; + + str = etpan_make_quoted_printable(DEFAULT_DISPLAY_CHARSET, UTF8Characters(), 1); + result = Data::dataWithBytes(str, (unsigned int) strlen(str) + 1); + free(str); + + return result; +} + +int String::compareWithCaseSensitive(String * otherString, bool caseSensitive) +{ + if ((unicodeCharacters() == NULL) && (otherString->unicodeCharacters() != NULL)) { + return 0; + } + + if (unicodeCharacters() == NULL) { + return -1; + } + + if (otherString->unicodeCharacters() == NULL) { + return -1; + } + + if (caseSensitive) { + return u_strcmp(unicodeCharacters(), otherString->unicodeCharacters()); + } + else { + return u_strcasecmp(unicodeCharacters(), otherString->unicodeCharacters(), 0); + } +} + +int String::compare(String * otherString) +{ + return compareWithCaseSensitive(otherString, true); +} + +int String::caseInsensitiveCompare(String * otherString) +{ + return compareWithCaseSensitive(otherString, false); +} + +String * String::lowercaseString() +{ + UErrorCode err; + String * result = (String *) copy()->autorelease(); + err = U_ZERO_ERROR; + u_strToLower(result->mUnicodeChars, result->mLength, + result->mUnicodeChars, result->mLength, + NULL, &err); + return result; +} + +String * String::uppercaseString() +{ + UErrorCode err; + String * result = (String *) copy()->autorelease(); + err = U_ZERO_ERROR; + u_strToUpper(result->mUnicodeChars, result->mLength, + result->mUnicodeChars, result->mLength, + NULL, &err); + return result; +} + +void String::appendBytes(const char * bytes, unsigned int length, const char * charset) +{ + UErrorCode err; + + err = U_ZERO_ERROR; + UConverter * converter = ucnv_open(charset, &err); + if (converter == NULL) { + MCLog("invalid charset %s %i", charset, err); + return; + } + + err = U_ZERO_ERROR; + int32_t destLength = ucnv_toUChars(converter, NULL, 0, + bytes, length, &err); + int32_t destCapacity = destLength + 1; + UChar * dest = (UChar *) malloc(destCapacity * sizeof(* dest)); + err = U_ZERO_ERROR; + destLength = ucnv_toUChars(converter, dest, destCapacity, bytes, length, &err); + dest[destLength] = 0; + + // Fix in case of bad conversion. + for(int32_t i = 0 ; i < destLength ; i ++) { + if (dest[i] == 0) { + dest[i] = ' '; + } + } + + appendCharactersLength(dest, destLength); + free(dest); + + ucnv_close(converter); +} + +String * String::extractedSubject() +{ + return extractedSubjectAndKeepBracket(false); +} + +String * String::extractedSubjectAndKeepBracket(bool keepBracket) +{ + char * result; + String * str; + + result = extract_subject((char *) UTF8Characters(), keepBracket); + str = String::stringWithUTF8Characters(result); + free(result); + + return str; +} + +String * String::uuidString() +{ + uuid_t uuid; + uuid_string_t uuidString; + + uuid_generate(uuid); + uuid_unparse_lower(uuid, uuidString); + return String::stringWithUTF8Characters(uuidString); +} + +unsigned int String::replaceOccurrencesOfString(String * occurrence, String * replacement) +{ + unsigned int count; + + if (occurrence->length() == 0) + return 0; + + count = 0; + UChar * p = mUnicodeChars; + while (1) { + UChar * location; + location = u_strstr(p, occurrence->unicodeCharacters()); + if (location == NULL) + break; + p = location + 1; + count ++; + } + + UChar * unicodeChars; + int delta = replacement->length() - occurrence->length(); + int modifiedLength = mLength + delta * count + 1; + unicodeChars = (UChar *) malloc(modifiedLength * sizeof(* unicodeChars)); + UChar * dest_p = unicodeChars; + p = mUnicodeChars; + while (1) { + UChar * location; + unsigned int count; + + location = u_strstr(p, occurrence->unicodeCharacters()); + if (location == NULL) + break; + count = (unsigned int) (location - p); + u_memcpy(dest_p, p, count); + dest_p += count; + p += count; + u_memcpy(dest_p, p, replacement->length()); + p += occurrence->length(); + dest_p += replacement->length(); + } + // copy remaining + u_strcpy(dest_p, p); + + return count; +} + +UChar String::characterAtIndex(unsigned int index) +{ + return mUnicodeChars[index]; +} + +void String::deleteCharactersInRange(Range range) +{ + if (range.index > mLength) + return; + + if (range.index + range.length > mLength) { + range.length = mLength - range.index; + } + + int32_t count = mLength - (range.index + range.length); + u_memmove(&mUnicodeChars[range.index], &mUnicodeChars[range.index + range.length], count); +} + +int String::locationOfString(String * occurrence) +{ + UChar * location; + location = u_strstr(mUnicodeChars, occurrence->unicodeCharacters()); + if (location == NULL) { + return -1; + } + + return (int) (location - mUnicodeChars); +} + +#pragma mark strip HTML + +struct parserState { + int level; + int enabled; + int disabledLevel; + String * result; + int logEnabled; + int hasQuote; + int quoteLevel; + bool hasText; + bool lastCharIsWhitespace; + bool showBlockQuote; + bool showLink; + bool hasReturnToLine; + Array * linkStack; + Array * paragraphSpacingStack; +}; + +static void appendQuote(struct parserState * state); + +static void charactersParsed(void * context, + const xmlChar * ch, int len) +/*" Callback function for stringByStrippingHTML. "*/ +{ + struct parserState * state; + + state = (struct parserState *) context; + String * result = state->result; + + if (!state->enabled) { + return; + } + + if (state->logEnabled) { + MCLog("text %s", ch); + } + String * modifiedString; + modifiedString = new String((const char *) ch, len); + //modifiedString->replaceOccurrencesOfString(MCSTR("\r\n"), MCSTR(" ")); + modifiedString->replaceOccurrencesOfString(MCSTR("\n"), MCSTR(" ")); + modifiedString->replaceOccurrencesOfString(MCSTR("\r"), MCSTR(" ")); + modifiedString->replaceOccurrencesOfString(MCSTR("\t"), MCSTR(" ")); + + UChar specialCh[2]; + specialCh[0] = 133; + specialCh[1] = 0; + modifiedString->replaceOccurrencesOfString(String::stringWithCharacters(specialCh), MCSTR(" ")); + + while (modifiedString->replaceOccurrencesOfString(MCSTR(" "), MCSTR(" ")) > 0) { + } + + if (modifiedString->length() > 0) { + if (state->lastCharIsWhitespace) { + if (modifiedString->characterAtIndex(0) == ' ') { + modifiedString->deleteCharactersInRange(RangeMake(0, 1)); + } + } + } + + if (modifiedString->length() > 0) { + bool lastIsWhiteSpace; + bool isWhiteSpace; + + isWhiteSpace = false; + lastIsWhiteSpace = false; + if (modifiedString->length() > 0) { + if (modifiedString->characterAtIndex(modifiedString->length() - 1) == ' ') { + lastIsWhiteSpace = true; + } + } + if (lastIsWhiteSpace && (modifiedString->length() == 1)) { + isWhiteSpace = true; + } + + if (isWhiteSpace) { + if (state->lastCharIsWhitespace) { + // do nothing + } + else if (!state->hasText) { + // do nothing + } + else { + result->appendString(MCSTR(" ")); + state->lastCharIsWhitespace = true; + state->hasText = true; + } + } + else { + if (!state->hasQuote) { + appendQuote(state); + state->hasQuote = true; + } + result->appendString(modifiedString); + state->lastCharIsWhitespace = lastIsWhiteSpace; + state->hasText = true; + } + } + modifiedString->release(); +} + +/* GCS: custom error function to ignore errors */ +static void structuredError(void * userData, + xmlErrorPtr error) +{ + /* ignore all errors */ + (void)userData; + (void)error; +} + +static void appendQuote(struct parserState * state) +{ + if (state->quoteLevel < 0) { + MCLog("error consistency in quote level"); + state->lastCharIsWhitespace = true; + return; + } + for(int i = 0 ; i < state->quoteLevel ; i ++) { + state->result->appendString(MCSTR("> ")); + } + state->lastCharIsWhitespace = true; +} + +static void returnToLine(struct parserState * state) +{ + if (!state->hasQuote) { + appendQuote(state); + state->hasQuote = true; + } + state->result->appendString(MCSTR("\n")); + state->hasText = false; + state->lastCharIsWhitespace = true; + state->hasQuote = false; + state->hasReturnToLine = false; +} + +static void returnToLineAtBeginningOfBlock(struct parserState * state) +{ + if (state->hasText) { + returnToLine(state); + } + state->hasQuote = false; +} + +static Set * blockElements(void) +{ + static Set * elements = NULL; + pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + pthread_mutex_lock(&lock); + if (elements == NULL) { + elements = new Set(); + elements->addObject(MCSTR("address")); + elements->addObject(MCSTR("div")); + elements->addObject(MCSTR("p")); + elements->addObject(MCSTR("h1")); + elements->addObject(MCSTR("h2")); + elements->addObject(MCSTR("h3")); + elements->addObject(MCSTR("h4")); + elements->addObject(MCSTR("h5")); + elements->addObject(MCSTR("h6")); + elements->addObject(MCSTR("pre")); + elements->addObject(MCSTR("ul")); + elements->addObject(MCSTR("ol")); + elements->addObject(MCSTR("li")); + elements->addObject(MCSTR("dl")); + elements->addObject(MCSTR("dt")); + elements->addObject(MCSTR("dd")); + elements->addObject(MCSTR("form")); + // tables + elements->addObject(MCSTR("col")); + elements->addObject(MCSTR("colgroup")); + elements->addObject(MCSTR("th")); + elements->addObject(MCSTR("tbody")); + elements->addObject(MCSTR("thead")); + elements->addObject(MCSTR("tfoot")); + elements->addObject(MCSTR("table")); + elements->addObject(MCSTR("tr")); + elements->addObject(MCSTR("td")); + } + pthread_mutex_unlock(&lock); + + return elements; +} + +static HashMap * dictionaryFromAttributes(const xmlChar ** atts) +{ + HashMap * result; + + if (atts == NULL) + return NULL; + + result = HashMap::hashMap(); + for(const xmlChar ** curAtt = atts ; * curAtt != NULL ; curAtt += 2) { + const xmlChar * attrName; + const xmlChar * attrValue; + String * name; + + attrName = * curAtt; + attrValue = * (curAtt + 1); + if ((attrName == NULL) || (attrValue == NULL)) + continue; + + name = String::stringWithUTF8Characters((const char *) attrName); + name = name->lowercaseString(); + result->setObjectForKey(name, String::stringWithUTF8Characters((const char *) attrValue)); + } + + return result; +} + +static void elementStarted(void * ctx, const xmlChar * name, const xmlChar ** atts) +{ + struct parserState * state; + + state = (struct parserState *) ctx; + + if (state->logEnabled) { + MCLog("parsed element %s", name); + } + + if (strcasecmp((const char *) name, "blockquote") == 0) { + state->quoteLevel ++; + } + else if (strcasecmp((const char *) name, "a") == 0) { + AutoreleasePool * pool; + String * link; + HashMap * attributes; + + pool = new AutoreleasePool(); + attributes = dictionaryFromAttributes(atts); + link = (String *) attributes->objectForKey(MCSTR("href")); + if (link == NULL) + link = MCSTR(""); + + Array * item; + item = new Array(); + item->addObject(link); + item->addObject(Value::valueWithUnsignedIntValue(state->result->length())); + state->linkStack->addObject(item); + item->release(); + pool->release(); + } + else if (strcasecmp((const char *) name, "p") == 0) { + bool hasSpacing; + String * style; + AutoreleasePool * pool; + HashMap * attributes; + + hasSpacing = true; + + pool = new AutoreleasePool(); + attributes = dictionaryFromAttributes(atts); + style = (String *) attributes->objectForKey(MCSTR("style")); + if (style->locationOfString(MCSTR("margin: 0.0px 0.0px 0.0px 0.0px;")) != -1) { + hasSpacing = false; + } + else if (style->locationOfString(MCSTR("margin: 0px 0px 0px 0px;")) != -1) { + hasSpacing = false; + } + else if (style->locationOfString(MCSTR("margin: 0.0px;")) != -1) { + hasSpacing = false; + } + else if (style->locationOfString(MCSTR("margin: 0px;")) != -1) { + hasSpacing = false; + } + pool->release(); + + state->paragraphSpacingStack->addObject(Value::valueWithBoolValue(hasSpacing)); + } + + if (state->enabled) { + if (state->level == 1) { + if (strcasecmp((const char *) name, "head") == 0) { + state->enabled = 0; + state->disabledLevel = state->level; + } + } + if (strcasecmp((const char *) name, "style") == 0) { + state->enabled = 0; + state->disabledLevel = state->level; + } + else if (strcasecmp((const char *) name, "script") == 0) { + state->enabled = 0; + state->disabledLevel = state->level; + } + else if (strcasecmp((const char *) name, "p") == 0) { + returnToLineAtBeginningOfBlock(state); + if (((Value *) state->paragraphSpacingStack->lastObject())->boolValue()) { + returnToLine(state); + } + } + else if (blockElements()->containsObject(String::stringWithUTF8Characters((const char *) name)->lowercaseString())) { + returnToLineAtBeginningOfBlock(state); + } + else if (strcasecmp((const char *) name, "blockquote") == 0) { + if (!state->showBlockQuote) { + AutoreleasePool * pool; + String * type; + bool cite; + HashMap * attributes; + + cite = false; + pool = new AutoreleasePool(); + attributes = dictionaryFromAttributes(atts); + type = (String *) attributes->objectForKey(MCSTR("type")); + if (type->caseInsensitiveCompare(MCSTR("cite")) == 0) { + cite = true; + } + pool->release(); + + if (cite) { + state->enabled = 0; + state->disabledLevel = state->level; + } + else { + returnToLineAtBeginningOfBlock(state); + } + } + else { + returnToLineAtBeginningOfBlock(state); + } + } + else if (strcasecmp((const char *) name, "br") == 0) { + returnToLine(state); + state->hasReturnToLine = true; + } + } + + state->level ++; +} + +static void elementEnded(void * ctx, const xmlChar * name) +{ + struct parserState * state; + + state = (struct parserState *) ctx; + + if (state->logEnabled) { + MCLog("ended element %s", name); + } + + if (strcasecmp((const char *) name, "blockquote") == 0) { + state->quoteLevel --; + } + + state->level --; + if (!state->enabled) { + if (state->level == state->disabledLevel) { + state->enabled = 1; + } + } + + bool hasReturnToLine; + + hasReturnToLine = false; + if (strcasecmp((const char *) name, "a") == 0) { + if (state->enabled) { + Array * item; + String * link; + unsigned int offset; + + item = (Array *) state->linkStack->lastObject(); + link = (String *) item->objectAtIndex(0); + offset = ((Value *) item->objectAtIndex(1))->unsignedIntValue(); + if (state->showLink) { + if (offset != state->result->length()) { + if (link->length() > 0) { + if (!state->result->hasSuffix(link)) { + state->result->appendUTF8Characters("("); + state->result->appendString(link); + state->result->appendUTF8Characters(")"); + state->hasText = true; + state->lastCharIsWhitespace = false; + } + } + } + } + } + + state->linkStack->removeObjectAtIndex(state->linkStack->count() - 1); + } + else if (strcasecmp((const char *) name, "p") == 0) { + if (state->enabled) { + if (((Value *) state->paragraphSpacingStack->lastObject())->boolValue()) { + returnToLine(state); + } + } + state->paragraphSpacingStack->removeObjectAtIndex(state->paragraphSpacingStack->count() - 1); + hasReturnToLine = true; + } + else if (blockElements()->containsObject(String::stringWithUTF8Characters((const char *) name)->lowercaseString())) { + hasReturnToLine = true; + } + else if (strcasecmp((const char *) name, "blockquote") == 0) { + hasReturnToLine = true; + } + + if (hasReturnToLine) { + if (state->enabled) { + if (!state->hasReturnToLine) { + returnToLine(state); + } + } + } +} + +static void commentParsed(void * ctx, const xmlChar * value) +{ + struct parserState * state; + + state = (struct parserState *) ctx; + + if (state->logEnabled) { + MCLog("comments %s", value); + } +} + +void initializeLibXML() +{ + static bool initDone = false; + static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + + pthread_mutex_lock(&lock); + if (!initDone) { + initDone = true; + xmlInitParser(); + + /* GCS: override structuredErrorFunc to mine so + I can ignore errors */ + xmlSetStructuredErrorFunc(xmlGenericErrorContext, + &structuredError); + } + pthread_mutex_unlock(&lock); +} + +String * String::flattenHTMLAndShowBlockquoteAndLink(bool showBlockquote, bool showLink) +/*" Interpretes the receiver als HTML, removes all tags + and returns the plain text. "*/ +{ + initializeLibXML(); + + int mem_base = xmlMemBlocks(); + String * result = String::string(); + xmlSAXHandler handler; + bzero(&handler, sizeof(xmlSAXHandler)); + handler.characters = &charactersParsed; + handler.startElement = elementStarted; + handler.endElement = elementEnded; + handler.comment = commentParsed; + struct parserState state; + state.result = result; + state.level = 0; + state.enabled = 1; + state.logEnabled = 0; + state.disabledLevel = 0; + state.quoteLevel = 0; + state.hasText = false; + state.hasQuote = false; + state.hasReturnToLine = false; + state.showBlockQuote = showBlockquote; + state.showLink = showLink; + state.lastCharIsWhitespace = true; + state.linkStack = new Array(); + state.paragraphSpacingStack = new Array(); + + htmlSAXParseDoc((xmlChar*) UTF8Characters(), "utf-8", &handler, &state); + + if (mem_base != xmlMemBlocks()) { + MCLog("Leak of %d blocks found in htmlSAXParseDoc", + xmlMemBlocks() - mem_base); + } + + state.paragraphSpacingStack->release(); + state.linkStack->release(); + + UChar ch[2]; + ch[0] = 160; + ch[1] = 0; + result->replaceOccurrencesOfString(String::stringWithCharacters(ch), MCSTR(" ")); + + return result; +} + +String * String::flattenHTMLAndShowBlockquote(bool showBlockquote) +{ + return flattenHTMLAndShowBlockquoteAndLink(showBlockquote, true); +} + +String * String::flattenHTML() +{ + return flattenHTMLAndShowBlockquote(true); +} + +bool String::hasSuffix(String * suffix) +{ + if (mLength > suffix->mLength) { + if (u_memcmp(suffix->mUnicodeChars + (mLength - suffix->mLength), + mUnicodeChars, suffix->mLength) == 0) { + return true; + } + } + return false; +} + +bool String::hasPrefix(String * prefix) +{ + if (mLength > prefix->mLength) { + if (u_memcmp(prefix->mUnicodeChars, mUnicodeChars, prefix->mLength) == 0) { + return true; + } + } + return false; +} + +String * String::lastPathComponent() +{ + UChar * component = u_strrchr(mUnicodeChars, '/'); + if (component == NULL) + return (String *) this->copy()->autorelease(); + return String::stringWithCharacters(component + 1); +} + +String * String::pathExtension() +{ + UChar * component = u_strrchr(mUnicodeChars, '.'); + if (component == NULL) + return MCSTR(""); + return String::stringWithCharacters(component + 1); +} + +Data * String::dataUsingEncoding(const char * charset) +{ + UErrorCode err; + Data * data; + + if (charset == NULL) { + charset = "utf-8"; + } + + err = U_ZERO_ERROR; + UConverter * converter = ucnv_open(charset, &err); + if (converter == NULL) { + MCLog("invalid charset %s %i", charset, err); + return NULL; + } + + err = U_ZERO_ERROR; + int32_t destLength = ucnv_fromUChars(converter, NULL, 0, mUnicodeChars, mLength, &err); + int32_t destCapacity = destLength + 1; + char * dest = (char *) malloc(destCapacity * sizeof(* dest)); + err = U_ZERO_ERROR; + destLength = ucnv_fromUChars(converter, dest, destCapacity, mUnicodeChars, mLength, &err); + dest[destLength] = 0; + + // Fix in case of bad conversion. + for(int32_t i = 0 ; i < destLength ; i ++) { + if (dest[i] == 0) { + dest[i] = ' '; + } + } + + data = Data::dataWithBytes(dest, destLength); + + free(dest); + + ucnv_close(converter); + + return data; +} + +const char * String::fileSystemRepresentation() +{ + return UTF8Characters(); +} + +String * String::stringWithFileSystemRepresentation(const char * filename) +{ + return stringWithUTF8Characters(filename); +} + +Array * String::componentsSeparatedByString(String * separator) +{ + UChar * p; + Array * result; + + result = Array::array(); + p = mUnicodeChars; + while (1) { + UChar * location; + location = u_strstr(p, separator->unicodeCharacters()); + if (location == NULL) { + break; + } + + unsigned int length = (unsigned int) (p - location); + String * value = new String(p, length); + result->addObject(value); + value->release(); + + p = location + separator->length(); + } + + return result; +} + +int String::intValue() +{ + return (int) strtol(UTF8Characters(), NULL, 10); +} + +unsigned int String::unsignedIntValue() +{ + return (unsigned int) strtoul(UTF8Characters(), NULL, 10); +} + +long String::longValue() +{ + return strtol(UTF8Characters(), NULL, 10); +} + +unsigned long String::unsignedLongValue() +{ + return strtoul(UTF8Characters(), NULL, 10); +} + +long long String::longLongValue() +{ + return strtoll(UTF8Characters(), NULL, 10); +} + +unsigned long long String::unsignedLongLongValue() +{ + return strtoull(UTF8Characters(), NULL, 10); +} + +Data * String::mUTF7EncodedData() +{ + return dataUsingEncoding("mutf-7"); +} + +String * String::stringWithMUTF7Data(Data * data) +{ + return data->stringWithCharset("mutf-7"); +} + +String * String::mUTF7EncodedString() +{ + Data * data = mUTF7EncodedData(); + return data->stringWithCharset("ascii"); +} + +String * String::mUTF7DecodedString() +{ + Data * data = dataUsingEncoding("utf-8"); + return stringWithMUTF7Data(data); +} + +String * String::substringFromIndex(unsigned int idx) +{ + return substringWithRange(RangeMake(idx, length() - idx)); +} + +String * String::substringToIndex(unsigned int idx) +{ + return substringWithRange(RangeMake(0, idx)); +} + +String * String::substringWithRange(Range range) +{ + if (range.index > length()) { + return MCSTR(""); + } + + if (range.index + range.length > length()) { + range.length = length() - range.index; + } + + return stringWithCharacters(unicodeCharacters() + range.index, range.length); +} + +static chash * uniquedStringHash = NULL; + +static void initUniquedStringHash() +{ + uniquedStringHash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY); +} + +String * String::uniquedStringWithUTF8Characters(const char * UTF8Characters) +{ + chashdatum key; + chashdatum value; + static pthread_once_t once = PTHREAD_ONCE_INIT; + int r; + + pthread_once(&once, initUniquedStringHash); + key.data = (void *) UTF8Characters; + key.len = (unsigned int) strlen(UTF8Characters); + r = chash_get(uniquedStringHash, &key, &value); + if (r == 0) { + return (String *) value.data; + } + else { + value.data = new String(UTF8Characters); + value.len = 0; + chash_set(uniquedStringHash, &key, &value, NULL); + return (String *) value.data; + } +} diff --git a/src/core/basetypes/MCString.h b/src/core/basetypes/MCString.h new file mode 100644 index 00000000..9ff04390 --- /dev/null +++ b/src/core/basetypes/MCString.h @@ -0,0 +1,123 @@ +#ifndef __MAILCORE_MCSTR_H_ + +#define __MAILCORE_MCSTR_H_ + +#include <mailcore/MCObject.h> +#include <mailcore/MCRange.h> + +#include <stdarg.h> +#include <unicode/utypes.h> + +namespace mailcore { + + class Data; + class Array; + + class String : public Object { + private: + UChar * mUnicodeChars; + unsigned int mLength; + unsigned int mAllocated; + void allocate(unsigned int length); + void appendCharactersLength(const UChar * unicodeCharacters, unsigned int length); + void reset(); + int compareWithCaseSensitive(String * otherString, bool caseSensitive); + void appendBytes(const char * bytes, unsigned int length, const char * charset); + void appendUTF8CharactersLength(const char * UTF8Characters, unsigned int length); + + public: + String(const UChar * unicodeChars = NULL); + String(const UChar * unicodeChars, unsigned int length); + String(const char * UTF8Characters); + String(String * otherString); + String(Data * data, const char * charset); + String(const char * bytes, unsigned int length, const char * charset = NULL); + virtual ~String(); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + virtual bool isEqual(Object * otherObject); + virtual unsigned int hash(); + + static String * string(); + static String * stringWithUTF8Format(const char * format, ...); + static String * stringWithVUTF8Format(const char * format, va_list ap); + static String * stringWithUTF8Characters(const char * UTF8Characters); + static String * stringWithCharacters(const UChar * characters); + static String * stringWithCharacters(const UChar * characters, unsigned int length); + + virtual const UChar * unicodeCharacters(); + virtual const char * UTF8Characters(); + virtual unsigned int length(); + + virtual void appendString(String * otherString); + virtual void appendUTF8Format(const char * format, ...); + virtual void appendCharacters(const UChar * unicodeCharacters); + virtual void appendUTF8Characters(const char * UTF8Characters); + virtual void setString(String * otherString); + virtual void setUTF8Characters(const char * UTF8Characters); + virtual void setCharacters(const UChar * unicodeCharacters); + + virtual String * stringByAppendingString(String * otherString); + virtual String * stringByAppendingUTF8Format(const char * format, ...); + virtual String * stringByAppendingUTF8Characters(const char * UTF8Characters); + virtual String * stringByAppendingCharacters(const UChar * unicodeCharacters); + + virtual int compare(String * otherString); + virtual int caseInsensitiveCompare(String * otherString); + virtual String * lowercaseString(); + virtual String * uppercaseString(); + + virtual UChar characterAtIndex(unsigned int idx); + virtual void deleteCharactersInRange(Range range); + virtual unsigned int replaceOccurrencesOfString(String * occurrence, String * replacement); + virtual int locationOfString(String * occurrence); + + virtual Array * componentsSeparatedByString(String * separator); + + // Additions + static String * stringByDecodingMIMEHeaderValue(const char * phrase); + virtual Data * encodedAddressDisplayNameValue(); + virtual Data * encodedMIMEHeaderValue(); + virtual Data * encodedMIMEHeaderValueForSubject(); + virtual String * extractedSubject(); + virtual String * extractedSubjectAndKeepBracket(bool keepBracket); + static String * uuidString(); + + virtual bool hasSuffix(String * suffix); + virtual bool hasPrefix(String * prefix); + + virtual String * substringFromIndex(unsigned int idx); + virtual String * substringToIndex(unsigned int idx); + virtual String * substringWithRange(Range range); + + virtual String * flattenHTML(); + virtual String * flattenHTMLAndShowBlockquote(bool showBlockquote); + virtual String * flattenHTMLAndShowBlockquoteAndLink(bool showBlockquote, bool showLink); + + virtual String * lastPathComponent(); + virtual String * pathExtension(); + virtual Data * dataUsingEncoding(const char * charset = NULL); + + virtual const char * fileSystemRepresentation(); + static String * stringWithFileSystemRepresentation(const char * filename); + + int intValue(); + unsigned int unsignedIntValue(); + long longValue(); + unsigned long unsignedLongValue(); + long long longLongValue(); + unsigned long long unsignedLongLongValue(); + + virtual Data * mUTF7EncodedData(); + static String * stringWithMUTF7Data(Data * data); + virtual String * mUTF7EncodedString(); + virtual String * mUTF7DecodedString(); + + static String * uniquedStringWithUTF8Characters(const char * UTF8Characters); + }; + +} + +#endif diff --git a/src/core/basetypes/MCUtils.h b/src/core/basetypes/MCUtils.h new file mode 100644 index 00000000..8dbbf5f4 --- /dev/null +++ b/src/core/basetypes/MCUtils.h @@ -0,0 +1,34 @@ +#ifndef __MAILCORE_MCUTILS_H + +#define __MAILCORE_MCUTILS_H + +#define MC_SAFE_RETAIN(o) ((o) != NULL ? (o)->retain() : NULL) +#define MC_SAFE_COPY(o) ((o) != NULL ? (o)->copy() : NULL) + +#define MC_SAFE_RELEASE(o) \ + do { \ + if ((o) != NULL) { \ + (o)->release(); \ + (o) = NULL; \ + } \ + } while (0) + +#define MC_SAFE_REPLACE_RETAIN(type, mField, value) \ + do { \ + MC_SAFE_RELEASE(mField); \ + mField = (type *) MC_SAFE_RETAIN(value); \ + } while (0) + +#define MC_SAFE_REPLACE_COPY(type, mField, value) \ + do { \ + MC_SAFE_RELEASE(mField); \ + mField = (type *) MC_SAFE_COPY(value); \ + } while (0) + +#define MCSTR(str) mailcore::String::uniquedStringWithUTF8Characters("" str "") + +#define MCUTF8(str) ((str) != NULL ? (str)->UTF8Characters() : NULL ) +#define MMCUTF8(str) MCUTF8(str) +#define MCUTF8DESC(obj) ((obj) != NULL ? (obj)->description()->UTF8Characters() : NULL ) + +#endif diff --git a/src/core/basetypes/MCValue.cc b/src/core/basetypes/MCValue.cc new file mode 100644 index 00000000..d275d3df --- /dev/null +++ b/src/core/basetypes/MCValue.cc @@ -0,0 +1,314 @@ +#include "MCValue.h" + +#include <string.h> +#include <stdlib.h> + +#include "MCString.h" +#include "MCHash.h" +#include "MCUtils.h" + +using namespace mailcore; + +enum { + VALUE_TYPE_NONE, + VALUE_TYPE_BOOL_VALUE, + VALUE_TYPE_CHAR_VALUE, + VALUE_TYPE_UNSIGNED_CHAR_VALUE, + VALUE_TYPE_SHORT_VALUE, + VALUE_TYPE_UNSIGNED_SHORT_VALUE, + VALUE_TYPE_INT_VALUE, + VALUE_TYPE_UNSIGNED_INT_VALUE, + VALUE_TYPE_LONG_VALUE, + VALUE_TYPE_UNSIGNED_LONG_VALUE, + VALUE_TYPE_LONG_LONG_VALUE, + VALUE_TYPE_UNSIGNED_LONG_LONG_VALUE, + VALUE_TYPE_FLOAT_VALUE, + VALUE_TYPE_DOUBLE_VALUE, + VALUE_TYPE_POINTER_VALUE, + VALUE_TYPE_DATA_VALUE, +}; + +Value::Value() +{ + mType = VALUE_TYPE_NONE; + memset(&mValue, 0, sizeof(mValue)); +} + +Value::Value(Value * other) +{ + memcpy(&mValue, &other->mValue, sizeof(mValue)); + mType = other->mType; + if (mType == VALUE_TYPE_DATA_VALUE) { + mValue.dataValue.data = (char *) malloc(mValue.dataValue.length); + memcpy(mValue.dataValue.data, other->mValue.dataValue.data, mValue.dataValue.length); + } +} + +Value::~Value() +{ + if (mType == VALUE_TYPE_DATA_VALUE) { + free(mValue.dataValue.data); + } +} + +String * Value::description() +{ + switch (mType) { + case VALUE_TYPE_BOOL_VALUE: + if (mValue.boolValue) { + return MCSTR("true"); + } + else { + return MCSTR("false"); + } + case VALUE_TYPE_CHAR_VALUE: + return String::stringWithUTF8Format("%i", (int) mValue.charValue); + case VALUE_TYPE_UNSIGNED_CHAR_VALUE: + return String::stringWithUTF8Format("%u", (unsigned int) mValue.unsignedCharValue); + case VALUE_TYPE_SHORT_VALUE: + return String::stringWithUTF8Format("%i", (int) mValue.shortValue); + case VALUE_TYPE_UNSIGNED_SHORT_VALUE: + return String::stringWithUTF8Format("%u", (unsigned int) mValue.unsignedShortValue); + case VALUE_TYPE_INT_VALUE: + return String::stringWithUTF8Format("%i", mValue.intValue); + case VALUE_TYPE_UNSIGNED_INT_VALUE: + return String::stringWithUTF8Format("%u", mValue.unsignedIntValue); + case VALUE_TYPE_LONG_VALUE: + return String::stringWithUTF8Format("%li", mValue.longValue); + case VALUE_TYPE_UNSIGNED_LONG_VALUE: + return String::stringWithUTF8Format("%lu", mValue.unsignedLongValue); + case VALUE_TYPE_LONG_LONG_VALUE: + return String::stringWithUTF8Format("%lli", mValue.longLongValue); + case VALUE_TYPE_UNSIGNED_LONG_LONG_VALUE: + return String::stringWithUTF8Format("%llu", mValue.unsignedLongLongValue); + case VALUE_TYPE_FLOAT_VALUE: + return String::stringWithUTF8Format("%f", (double) mValue.floatValue); + case VALUE_TYPE_DOUBLE_VALUE: + return String::stringWithUTF8Format("%f", mValue.doubleValue); + case VALUE_TYPE_POINTER_VALUE: + return String::stringWithUTF8Format("%p", mValue.pointerValue); + case VALUE_TYPE_DATA_VALUE: + return String::stringWithUTF8Format("<Value:%p:data>", this); + default: + return String::stringWithUTF8Format("<Value:%p:unknown>", this); + } +} + +#if 0 +String * Value::className() +{ + return MCSTR("Value"); +} +#endif + +bool Value::isEqual(Object * otherObject) +{ + Value * otherValue = (Value *) otherObject; + if (otherValue->mType != mType) + return false; + if (mType == VALUE_TYPE_DATA_VALUE) { + if (mValue.dataValue.length != otherValue->mValue.dataValue.length) + return false; + if (memcmp(&otherValue->mValue.dataValue.data, &mValue.dataValue.data, mValue.dataValue.length) != 0) + return false; + } + else { + if (memcmp(&otherValue->mValue, &mValue, sizeof(mValue)) != 0) + return false; + } + return true; +} + +unsigned int Value::hash() +{ + return hashCompute((const char *) &mValue, sizeof(mValue)); +} + +Object * Value::copy() +{ + return new Value(this); +} + +Value * Value::valueWithBoolValue(bool value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_BOOL_VALUE; + result->mValue.boolValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithCharValue(char value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_CHAR_VALUE; + result->mValue.charValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithUnsignedCharValue(unsigned char value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_UNSIGNED_CHAR_VALUE; + result->mValue.unsignedCharValue = value; + return (Value *) result->autorelease(); +} + +/////////////////////// + +Value * Value::valueWithIntValue(int value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_INT_VALUE; + result->mValue.intValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithUnsignedIntValue(unsigned int value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_UNSIGNED_INT_VALUE; + result->mValue.unsignedIntValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithLongValue(long value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_LONG_VALUE; + result->mValue.longValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithUnsignedLongValue(unsigned long value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_UNSIGNED_LONG_VALUE; + result->mValue.unsignedLongValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithLongLongValue(long long value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_LONG_LONG_VALUE; + result->mValue.longLongValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithUnsignedLongLongValue(unsigned long long value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_UNSIGNED_LONG_LONG_VALUE; + result->mValue.unsignedLongLongValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithFloatValue(float value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_FLOAT_VALUE; + result->mValue.floatValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithDoubleValue(double value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_DOUBLE_VALUE; + result->mValue.doubleValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithPointerValue(void * value) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_POINTER_VALUE; + result->mValue.pointerValue = value; + return (Value *) result->autorelease(); +} + +Value * Value::valueWithData(const char * value, int length) +{ + Value * result = new Value(); + result->mType = VALUE_TYPE_DATA_VALUE; + result->mValue.dataValue.data = (char *) malloc(length); + memcpy(result->mValue.dataValue.data, value, length); + result->mValue.dataValue.length = length; + return (Value *) result->autorelease(); +} + +bool Value::boolValue() +{ + return mValue.boolValue; +} + +char Value::charValue() +{ + return mValue.charValue; +} + +unsigned char Value::unsignedCharValue() +{ + return mValue.unsignedCharValue; +} + +short Value::shortValue() +{ + return mValue.shortValue; +} + +unsigned short Value::unsignedShortValue() +{ + return mValue.unsignedShortValue; +} + +int Value::intValue() +{ + return mValue.intValue; +} + +unsigned int Value::unsignedIntValue() +{ + return mValue.unsignedIntValue; +} + +long Value::longValue() +{ + return mValue.longValue; +} + +unsigned long Value::unsignedLongValue() +{ + return mValue.unsignedLongValue; +} + +long long Value::longLongValue() +{ + return mValue.longLongValue; +} + +unsigned long long Value::unsignedLongLongValue() +{ + return mValue.unsignedLongLongValue; +} + +float Value::floatValue() +{ + return mValue.floatValue; +} + +double Value::doubleValue() +{ + return mValue.doubleValue; +} + +void * Value::pointerValue() +{ + return mValue.pointerValue; +} + +void Value::dataValue(const char ** p_value, int * p_length) +{ + * p_value = mValue.dataValue.data; + * p_length = mValue.dataValue.length; +} diff --git a/src/core/basetypes/MCValue.h b/src/core/basetypes/MCValue.h new file mode 100644 index 00000000..cc776b24 --- /dev/null +++ b/src/core/basetypes/MCValue.h @@ -0,0 +1,81 @@ +#ifndef __MAILCORE_MCVALUE_H_ + +#define __MAILCORE_MCVALUE_H_ + +#include <mailcore/MCObject.h> + +namespace mailcore { + + class String; + + class Value : public Object { + private: + int mType; + union { + bool boolValue; + char charValue; + unsigned char unsignedCharValue; + short shortValue; + unsigned short unsignedShortValue; + int intValue; + unsigned int unsignedIntValue; + long longValue; + unsigned long unsignedLongValue; + long long longLongValue; + unsigned long long unsignedLongLongValue; + float floatValue; + double doubleValue; + void * pointerValue; + struct { + char * data; + int length; + } dataValue; + } mValue; + Value(); + + public: + Value(Value * other); + virtual ~Value(); + + virtual String * description(); + //virtual String * className(); + + virtual bool isEqual(Object * otherObject); + virtual unsigned int hash(); + + Object * copy(); + + static Value * valueWithBoolValue(bool value); + static Value * valueWithCharValue(char value); + static Value * valueWithUnsignedCharValue(unsigned char value); + static Value * valueWithIntValue(int value); + static Value * valueWithUnsignedIntValue(unsigned int value); + static Value * valueWithLongValue(long value); + static Value * valueWithUnsignedLongValue(unsigned long value); + static Value * valueWithLongLongValue(long long value); + static Value * valueWithUnsignedLongLongValue(unsigned long long value); + static Value * valueWithFloatValue(float value); + static Value * valueWithDoubleValue(double value); + static Value * valueWithPointerValue(void * value); + static Value * valueWithData(const char * value, int length); + + virtual bool boolValue(); + virtual char charValue(); + virtual unsigned char unsignedCharValue(); + virtual short shortValue(); + virtual unsigned short unsignedShortValue(); + virtual int intValue(); + virtual unsigned int unsignedIntValue(); + virtual long longValue(); + virtual unsigned long unsignedLongValue(); + virtual long long longLongValue(); + virtual unsigned long long unsignedLongLongValue(); + virtual float floatValue(); + virtual double doubleValue(); + virtual void * pointerValue(); + virtual void dataValue(const char ** p_value, int * p_length); + }; + +} + +#endif diff --git a/src/core/imap/.DS_Store b/src/core/imap/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/src/core/imap/.DS_Store diff --git a/src/core/imap/MCIMAP.h b/src/core/imap/MCIMAP.h new file mode 100644 index 00000000..34b2d435 --- /dev/null +++ b/src/core/imap/MCIMAP.h @@ -0,0 +1,16 @@ +#ifndef __MAILCORE_MCIMAP_H_ + +#define __MAILCORE_MCIMAP_H_ + +#include <mailcore/MCIMAPFolder.h> +#include <mailcore/MCIMAPMessage.h> +#include <mailcore/MCIMAPMessagePart.h> +#include <mailcore/MCIMAPMultipart.h> +#include <mailcore/MCIMAPNamespace.h> +#include <mailcore/MCIMAPNamespaceItem.h> +#include <mailcore/MCIMAPPart.h> +#include <mailcore/MCIMAPProgressCallback.h> +#include <mailcore/MCIMAPSearchExpression.h> +#include <mailcore/MCIMAPSession.h> + +#endif diff --git a/src/core/imap/MCIMAPFolder.cc b/src/core/imap/MCIMAPFolder.cc new file mode 100644 index 00000000..0f1b2105 --- /dev/null +++ b/src/core/imap/MCIMAPFolder.cc @@ -0,0 +1,71 @@ +#include "MCIMAPFolder.h" + +using namespace mailcore; + +void IMAPFolder::init() +{ + mPath = NULL; + mDelimiter = 0; + mFlags = IMAPFolderFlagNone; +} + +IMAPFolder::IMAPFolder() +{ + init(); +} + +IMAPFolder::IMAPFolder(IMAPFolder * other) +{ + init(); + setPath(other->path()); + setDelimiter(other->delimiter()); + setFlags(other->flags()); +} + +IMAPFolder::~IMAPFolder() +{ + MC_SAFE_RELEASE(mPath); +} + +#if 0 +String * IMAPFolder::className() +{ + return MCSTR("IMAPFolder"); +} +#endif + +Object * IMAPFolder::copy() +{ + return new IMAPFolder(this); +} + +void IMAPFolder::setPath(String * path) +{ + MC_SAFE_REPLACE_COPY(String, mPath, path); +} + +String * IMAPFolder::path() +{ + return mPath; +} + +void IMAPFolder::setDelimiter(char delimiter) +{ + mDelimiter = delimiter; +} + +char IMAPFolder::delimiter() +{ + return mDelimiter; +} + +void IMAPFolder::setFlags(IMAPFolderFlag flags) +{ + mFlags = flags; +} + +IMAPFolderFlag IMAPFolder::flags() +{ + return mFlags; +} + diff --git a/src/core/imap/MCIMAPFolder.h b/src/core/imap/MCIMAPFolder.h new file mode 100644 index 00000000..f68498c2 --- /dev/null +++ b/src/core/imap/MCIMAPFolder.h @@ -0,0 +1,36 @@ +#ifndef __MAILCORE_MCIMAPFOLDER_H + +#define __MAILCORE_MCIMAPFOLDER_H + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCMessageConstants.h> + +namespace mailcore { + + class IMAPFolder : public Object { + private: + String * mPath; + char mDelimiter; + IMAPFolderFlag mFlags; + void init(); + public: + IMAPFolder(); + IMAPFolder(IMAPFolder * other); + virtual ~IMAPFolder(); + + //virtual String * className(); + virtual Object * copy(); + + virtual void setPath(String * path); + virtual String * path(); + + virtual void setDelimiter(char delimiter); + virtual char delimiter(); + + virtual void setFlags(IMAPFolderFlag flags); + virtual IMAPFolderFlag flags(); + }; + +} + +#endif diff --git a/src/core/imap/MCIMAPMessage.cc b/src/core/imap/MCIMAPMessage.cc new file mode 100644 index 00000000..faf91c66 --- /dev/null +++ b/src/core/imap/MCIMAPMessage.cc @@ -0,0 +1,111 @@ +#include "MCIMAPMessage.h" + +#include "MCMessageHeader.h" + +using namespace mailcore; + +void IMAPMessage::init() +{ + mUid = NULL; + mFlags = MessageFlagNone; + mOriginalFlags = MessageFlagNone; + mMainPart = NULL; + mLabels = NULL; +} + +IMAPMessage::IMAPMessage() +{ + init(); +} + +IMAPMessage::IMAPMessage(IMAPMessage * other) +{ + init(); + setUid(other->uid()); + setFlags(other->flags()); + setOriginalFlags(other->originalFlags()); + setMainPart((AbstractPart *) other->mainPart()->copy()->autorelease()); + setGmailLabels(other->gmailLabels()); +} + +IMAPMessage::~IMAPMessage() +{ + MC_SAFE_RELEASE(mMainPart); + MC_SAFE_RELEASE(mLabels); +} + +#if 0 +String * IMAPMessage::className() +{ + return MCSTR("IMAPMessage"); +} +#endif + +Object * IMAPMessage::copy() +{ + return new IMAPMessage(this); +} + +String * IMAPMessage::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p %u\n", className()->UTF8Characters(), this, (unsigned int) uid()); + result->appendString(header()->description()); + if (mainPart() != NULL) { + result->appendString(mainPart()->description()); + result->appendUTF8Characters("\n"); + } + result->appendUTF8Characters(">"); + return result; +} + +uint32_t IMAPMessage::uid() +{ + return mUid; +} + +void IMAPMessage::setUid(uint32_t uid) +{ + mUid = uid; +} + +void IMAPMessage::setFlags(MessageFlag flags) +{ + mFlags = flags; +} + +MessageFlag IMAPMessage::flags() +{ + return mFlags; +} + +void IMAPMessage::setOriginalFlags(MessageFlag flags) +{ + mOriginalFlags = flags; +} + +MessageFlag IMAPMessage::originalFlags() +{ + return mOriginalFlags; +} + +void IMAPMessage::setMainPart(AbstractPart * mainPart) +{ + MC_SAFE_REPLACE_RETAIN(AbstractPart, mMainPart, mainPart); +} + +AbstractPart * IMAPMessage::mainPart() +{ + return mMainPart; +} + +void IMAPMessage::setGmailLabels(Array * labels) +{ + MC_SAFE_REPLACE_COPY(Array, mLabels, labels); +} + +Array * IMAPMessage::gmailLabels() +{ + return mLabels; +} + diff --git a/src/core/imap/MCIMAPMessage.h b/src/core/imap/MCIMAPMessage.h new file mode 100644 index 00000000..afaa07c8 --- /dev/null +++ b/src/core/imap/MCIMAPMessage.h @@ -0,0 +1,47 @@ +#ifndef __MAILCORE_IMAP_MESSAGE_H_ + +#define __MAILCORE_IMAP_MESSAGE_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractMessage.h> +#include <mailcore/MCMessageConstants.h> +#include <mailcore/MCAbstractPart.h> + +namespace mailcore { + + class IMAPMessage : public AbstractMessage { + private: + uint32_t mUid; + MessageFlag mFlags; + MessageFlag mOriginalFlags; + AbstractPart * mMainPart; + Array * mLabels; + void init(); + public: + IMAPMessage(); + IMAPMessage(IMAPMessage * other); + ~IMAPMessage(); + + //virtual String * className(); + virtual Object * copy(); + virtual String * description(); + + virtual uint32_t uid(); + virtual void setUid(uint32_t uid); + + virtual void setFlags(MessageFlag flags); + virtual MessageFlag flags(); + + virtual void setOriginalFlags(MessageFlag flags); + virtual MessageFlag originalFlags(); + + virtual void setMainPart(AbstractPart * mainPart); + virtual AbstractPart * mainPart(); + + virtual void setGmailLabels(Array * labels); + virtual Array * gmailLabels(); + }; + +} + +#endif diff --git a/src/core/imap/MCIMAPMessagePart.cc b/src/core/imap/MCIMAPMessagePart.cc new file mode 100644 index 00000000..31a6f7e5 --- /dev/null +++ b/src/core/imap/MCIMAPMessagePart.cc @@ -0,0 +1,27 @@ +#include "MCIMAPMessagePart.h" + +using namespace mailcore; + +IMAPMessagePart::IMAPMessagePart() +{ +} + +IMAPMessagePart::IMAPMessagePart(IMAPMessagePart * other) : AbstractMessagePart(other) +{ +} + +IMAPMessagePart::~IMAPMessagePart() +{ +} + +#if 0 +String * IMAPMessagePart::className() +{ + return MCSTR("IMAPMessagePart"); +} +#endif + +Object * IMAPMessagePart::copy() +{ + return new IMAPMessagePart(this); +} diff --git a/src/core/imap/MCIMAPMessagePart.h b/src/core/imap/MCIMAPMessagePart.h new file mode 100644 index 00000000..37e12fea --- /dev/null +++ b/src/core/imap/MCIMAPMessagePart.h @@ -0,0 +1,23 @@ +#ifndef __MAILCORE_IMAPMESSAGEPART_H_ + +#define __MAILCORE_IMAPMESSAGEPART_H_ + +#include <mailcore/MCAbstractMessagePart.h> + +namespace mailcore { + + class IMAPMessagePart : public AbstractMessagePart { + private: + + public: + IMAPMessagePart(); + IMAPMessagePart(IMAPMessagePart * other); + virtual ~IMAPMessagePart(); + + //virtual String * className(); + virtual Object * copy(); + }; +} + + +#endif diff --git a/src/core/imap/MCIMAPMultipart.cc b/src/core/imap/MCIMAPMultipart.cc new file mode 100644 index 00000000..a941cbc4 --- /dev/null +++ b/src/core/imap/MCIMAPMultipart.cc @@ -0,0 +1,28 @@ +#include "MCIMAPMultipart.h" + +using namespace mailcore; + +IMAPMultipart::IMAPMultipart() +{ +} + +IMAPMultipart::IMAPMultipart(IMAPMultipart * other) : AbstractMultipart(other) +{ +} + +IMAPMultipart::~IMAPMultipart() +{ +} + +#if 0 +String * IMAPMultipart::className() +{ + return MCSTR("IMAPMultipart"); +} +#endif + +Object * IMAPMultipart::copy() +{ + return new IMAPMultipart(this); +} + diff --git a/src/core/imap/MCIMAPMultipart.h b/src/core/imap/MCIMAPMultipart.h new file mode 100644 index 00000000..bdb33470 --- /dev/null +++ b/src/core/imap/MCIMAPMultipart.h @@ -0,0 +1,22 @@ +#ifndef __MAILCORE_MCIMAPMULTIPART_H + +#define __MAILCORE_MCIMAPMULTIPART_H + +#include <mailcore/MCAbstractMultipart.h> + +namespace mailcore { + + class IMAPMultipart : public AbstractMultipart { + private: + + public: + IMAPMultipart(); + IMAPMultipart(IMAPMultipart * other); + virtual ~IMAPMultipart(); + + //virtual String * className(); + virtual Object * copy(); + }; +} + +#endif diff --git a/src/core/imap/MCIMAPNamespace.cc b/src/core/imap/MCIMAPNamespace.cc new file mode 100644 index 00000000..b18752e4 --- /dev/null +++ b/src/core/imap/MCIMAPNamespace.cc @@ -0,0 +1,144 @@ +#include "MCIMAPNamespace.h" + +#include "MCIMAPNamespaceItem.h" + +using namespace mailcore; + +void IMAPNamespace::init() +{ + mItems = NULL; +} + +IMAPNamespace::IMAPNamespace() +{ + init(); + mItems = new Array(); +} + +IMAPNamespace::IMAPNamespace(IMAPNamespace * other) +{ + init(); + mItems = (Array *) other->mItems->copy(); +} + +IMAPNamespace::~IMAPNamespace() +{ + MC_SAFE_RELEASE(mItems); +} + +#if 0 +String * IMAPNamespace::className() +{ + return MCSTR("IMAPNamespace"); +} +#endif + +String * IMAPNamespace::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p %s>", MCUTF8(className()), this, + MCUTF8DESC(mItems)); + return result; +} + +Object * IMAPNamespace::copy() +{ + return new IMAPNamespace(this); +} + +IMAPNamespaceItem * IMAPNamespace::mainItem() +{ + if (mItems->count() == 0) + return NULL; + + return (IMAPNamespaceItem *) mItems->objectAtIndex(0); +} + +IMAPNamespaceItem * IMAPNamespace::itemForPath(String * path) +{ + for(unsigned int i = 0 ; i < mItems->count() ; i ++) { + IMAPNamespaceItem * item = (IMAPNamespaceItem *) mItems->objectAtIndex(i); + if (item->containsFolder(path)) + return item; + } + + return NULL; +} + +String * IMAPNamespace::mainPrefix() +{ + if (mItems->count() == 0) + return NULL; + + return mainItem()->prefix(); +} + +char IMAPNamespace::mainDelimiter() +{ + if (mItems->count() == 0) + return NULL; + + return mainItem()->delimiter(); +} + +Array * IMAPNamespace::prefixes() +{ + Array * result = Array::array(); + for(unsigned int i = 0 ; i < mItems->count() ; i ++) { + IMAPNamespaceItem * item = (IMAPNamespaceItem *) mItems->objectAtIndex(i); + result->addObject(item->prefix()); + } + return result; +} + +String * IMAPNamespace::pathForComponents(Array * components) +{ + return mainItem()->pathForComponents(components); +} + +String * IMAPNamespace::pathForComponentsAndPrefix(Array * components, String * prefix) +{ + return itemForPath(prefix)->pathForComponents(components); +} + +Array * IMAPNamespace::componentsFromPath(String * path) +{ + IMAPNamespaceItem * item = itemForPath(path); + return item->componentsForPath(path); +} + +bool IMAPNamespace::containsFolderPath(String * path) +{ + return (itemForPath(path) != NULL); +} + +IMAPNamespace * IMAPNamespace::namespaceWithPrefix(String * prefix, char delimiter) +{ + IMAPNamespace * ns; + IMAPNamespaceItem * item; + + ns = new IMAPNamespace(); + item = new IMAPNamespaceItem(); + item->setDelimiter(delimiter); + item->setPrefix(prefix); + ns->mItems->addObject(item); + item->release(); + + return (IMAPNamespace *) ns->autorelease(); +} + +void IMAPNamespace::importIMAPNamespace(struct mailimap_namespace_item * item) +{ + clistiter * cur; + + for(cur = clist_begin(item->ns_data_list) ; cur != NULL ; cur = clist_next(cur)) { + IMAPNamespaceItem * item; + struct mailimap_namespace_info * info; + + info = (struct mailimap_namespace_info *) clist_content(cur); + item = new IMAPNamespaceItem(); + item->importIMAPNamespaceInfo(info); + mItems->addObject(item); + item->release(); + } +} diff --git a/src/core/imap/MCIMAPNamespace.h b/src/core/imap/MCIMAPNamespace.h new file mode 100644 index 00000000..b9cbf92a --- /dev/null +++ b/src/core/imap/MCIMAPNamespace.h @@ -0,0 +1,44 @@ +#ifndef __MAILCORE_MCIMAPNAMESPACE_H_ + +#define __MAILCORE_MCIMAPNAMESPACE_H_ + +#include <mailcore/MCBaseTypes.h> + +namespace mailcore { + + class IMAPNamespaceItem; + + class IMAPNamespace : public Object { + private: + Array * mItems; + void init(); + IMAPNamespaceItem * mainItem(); + IMAPNamespaceItem * itemForPath(String * path); + public: + IMAPNamespace(); + IMAPNamespace(IMAPNamespace * other); + virtual ~IMAPNamespace(); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + + virtual String * mainPrefix(); + virtual char mainDelimiter(); + + virtual Array * prefixes(); + + virtual String * pathForComponents(Array * components); + virtual String * pathForComponentsAndPrefix(Array * components, String * prefix); + + virtual Array * componentsFromPath(String * path); + + virtual bool containsFolderPath(String * path); + + static IMAPNamespace * namespaceWithPrefix(String * prefix, char delimiter); + virtual void importIMAPNamespace(struct mailimap_namespace_item * item); + }; + +} + +#endif diff --git a/src/core/imap/MCIMAPNamespaceItem.cc b/src/core/imap/MCIMAPNamespaceItem.cc new file mode 100644 index 00000000..62fca1f0 --- /dev/null +++ b/src/core/imap/MCIMAPNamespaceItem.cc @@ -0,0 +1,147 @@ +#include "MCIMAPNamespaceItem.h" + +using namespace mailcore; + +static Array * encodedComponents(Array * components); +static Array * decodedComponents(Array * components); + +void IMAPNamespaceItem::init() +{ + mDelimiter = 0; + mPrefix = NULL; +} + +IMAPNamespaceItem::IMAPNamespaceItem() +{ + init(); +} + +IMAPNamespaceItem::IMAPNamespaceItem(IMAPNamespaceItem * other) +{ + init(); + setDelimiter(other->delimiter()); + setPrefix(other->prefix()); +} + +IMAPNamespaceItem::~IMAPNamespaceItem() +{ + MC_SAFE_RELEASE(mPrefix); +} + +#if 0 +String * IMAPNamespaceItem::className() +{ + return MCSTR("IMAPNamespaceItem"); +} +#endif + +String * IMAPNamespaceItem::description() +{ + return String::stringWithUTF8Format("<%s:%p %s %c>", + MCUTF8(className()), this, MCUTF8(prefix()), delimiter()); +} + +Object * IMAPNamespaceItem::copy() +{ + return new IMAPNamespaceItem(this); +} + +void IMAPNamespaceItem::setPrefix(String * prefix) +{ + MC_SAFE_REPLACE_COPY(String, mPrefix, prefix); +} + +String * IMAPNamespaceItem::prefix() +{ + return mPrefix; +} + +void IMAPNamespaceItem::setDelimiter(char delimiter) +{ + mDelimiter = delimiter; +} + +char IMAPNamespaceItem::delimiter() +{ + return mDelimiter; +} + +String * IMAPNamespaceItem::pathForComponents(Array * components) +{ + String * path; + String * prefix; + + components = encodedComponents(components); + path = components->componentsJoinedByString(String::stringWithUTF8Format("%c", mDelimiter)); + + prefix = mPrefix; + if (prefix->length() > 0) { + if (!prefix->hasSuffix(String::stringWithUTF8Format("%c", mDelimiter))) { + prefix = prefix->stringByAppendingUTF8Format("%c", mDelimiter); + } + } + return prefix->stringByAppendingString(path); +} + +Array * IMAPNamespaceItem::componentsForPath(String * path) +{ + Array * components; + + if (path->hasPrefix(mPrefix)) { + path = path->substringFromIndex(mPrefix->length()); + } + components = path->componentsSeparatedByString(String::stringWithUTF8Format("%c", mDelimiter)); + components = decodedComponents(components); + if (components->count() > 0) { + if (((String *) components->objectAtIndex(0))->length() == 0) { + components->removeObjectAtIndex(0); + } + } + + return components; +} + +bool IMAPNamespaceItem::containsFolder(String * folder) +{ + if (mPrefix->length() == 0) + return true; + return folder->hasPrefix(mPrefix); +} + +void IMAPNamespaceItem::importIMAPNamespaceInfo(struct mailimap_namespace_info * info) +{ + setPrefix(String::stringWithUTF8Characters(info->ns_prefix)); + setDelimiter(info->ns_delimiter); +} + +static Array * encodedComponents(Array * components) +{ + Array * result; + + result = Array::array(); + for(unsigned int i = 0 ; i < components->count() ; i ++) { + String * value = (String *) components->objectAtIndex(i); + result->addObject(value->mUTF7EncodedString()); + } + + return result; +} + +static Array * decodedComponents(Array * components) +{ + Array * result; + + result = Array::array(); + for(unsigned int i = 0 ; i < components->count() ; i ++) { + String * value = (String *) components->objectAtIndex(i); + String * decoded; + + decoded = value->mUTF7DecodedString(); + if (decoded == NULL) { + decoded = value; + } + result->addObject(decoded); + } + + return result; +} diff --git a/src/core/imap/MCIMAPNamespaceItem.h b/src/core/imap/MCIMAPNamespaceItem.h new file mode 100644 index 00000000..3b40ae7c --- /dev/null +++ b/src/core/imap/MCIMAPNamespaceItem.h @@ -0,0 +1,39 @@ +#ifndef __MAILCORE_MCIMAPNAMESPACEITEM_H + +#define __MAILCORE_MCIMAPNAMESPACEITEM_H + +#include <mailcore/MCBaseTypes.h> + +namespace mailcore { + + class IMAPNamespaceItem : public Object { + private: + char mDelimiter; + String * mPrefix; + void init(); + public: + IMAPNamespaceItem(); + IMAPNamespaceItem(IMAPNamespaceItem * other); + virtual ~IMAPNamespaceItem(); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + + virtual void setPrefix(String * prefix); + virtual String * prefix(); + + virtual void setDelimiter(char delimiter); + virtual char delimiter(); + + virtual String * pathForComponents(Array * components); + virtual Array * componentsForPath(String * path); + + virtual bool containsFolder(String * folder); + + virtual void importIMAPNamespaceInfo(struct mailimap_namespace_info * info); + }; + +} + +#endif diff --git a/src/core/imap/MCIMAPPart.cc b/src/core/imap/MCIMAPPart.cc new file mode 100644 index 00000000..110c8fa0 --- /dev/null +++ b/src/core/imap/MCIMAPPart.cc @@ -0,0 +1,285 @@ +#include "MCIMAPPart.h" + +#include <string.h> + +#include "MCIMAPMessagePart.h" +#include "MCIMAPMultipart.h" +#include "MCMessageHeader.h" + +using namespace mailcore; + +void IMAPPart::init() +{ + mPartID = NULL; + mEncoding = Encoding8Bit; + mSize = 0; +} + +IMAPPart::IMAPPart() +{ + init(); +} + +IMAPPart::IMAPPart(IMAPPart * other) : AbstractPart(other) +{ + init(); + MC_SAFE_REPLACE_COPY(String, mPartID, other->mPartID); + mEncoding = other->mEncoding; + mSize = other->mSize; +} + +IMAPPart::~IMAPPart() +{ + MC_SAFE_RELEASE(mPartID); +} + +#if 0 +String * IMAPPart::className() +{ + return MCSTR("IMAPPart"); +} +#endif + +Object * IMAPPart::copy() +{ + return new IMAPPart(this); +} + +void IMAPPart::setPartID(String * partID) +{ + MC_SAFE_REPLACE_COPY(String, mPartID, partID); +} + +String * IMAPPart::partID() +{ + return mPartID; +} + +void IMAPPart::setSize(unsigned int size) +{ + mSize = size; +} + +unsigned int IMAPPart::size() +{ + return mSize; +} + +void IMAPPart::setEncoding(Encoding encoding) +{ + mEncoding = encoding; +} + +Encoding IMAPPart::encoding() +{ + return mEncoding; +} + +unsigned int IMAPPart::decodedSize() +{ + switch (mEncoding) { + case MAILIMAP_BODY_FLD_ENC_BASE64: + return mSize * 3 / 4; + + default: + return mSize; + } +} + +AbstractPart * IMAPPart::attachmentWithIMAPBody(struct mailimap_body * body) +{ + String * partID; + + partID = NULL; + if (body->bd_type == MAILIMAP_BODY_1PART) { + partID = MCSTR("1"); + } + return attachmentWithIMAPBodyInternal(body, partID); +} + +AbstractPart * IMAPPart::attachmentWithIMAPBodyInternal(struct mailimap_body * body, String * partID) +{ + switch (body->bd_type) { + case MAILIMAP_BODY_1PART: + return attachmentWithIMAPBody1Part(body->bd_data.bd_body_1part, partID); + case MAILIMAP_BODY_MPART: + return attachmentWithIMAPBodyMultipart(body->bd_data.bd_body_mpart, partID); + } + + return NULL; +} + +AbstractPart * IMAPPart::attachmentWithIMAPBody1Part(struct mailimap_body_type_1part * body_1part, + String * partID) +{ + switch (body_1part->bd_type) { + case MAILIMAP_BODY_TYPE_1PART_BASIC: + { + IMAPPart * attachment; + + attachment = attachmentWithIMAPBody1PartBasic(body_1part->bd_data.bd_type_basic, + body_1part->bd_ext_1part); + attachment->setPartID(partID); + return attachment; + } + case MAILIMAP_BODY_TYPE_1PART_MSG: + { + return attachmentWithIMAPBody1PartMessage(body_1part->bd_data.bd_type_msg, + body_1part->bd_ext_1part, partID); + } + case MAILIMAP_BODY_TYPE_1PART_TEXT: + { + IMAPPart * attachment; + + attachment = attachmentWithIMAPBody1PartText(body_1part->bd_data.bd_type_text, + body_1part->bd_ext_1part); + attachment->setPartID(partID); + MCLog("attachment %s", MCUTF8(partID)); + return attachment; + } + } + + return NULL; +} + +IMAPMessagePart * IMAPPart::attachmentWithIMAPBody1PartMessage(struct mailimap_body_type_msg * message, + struct mailimap_body_ext_1part * extension, + String * partID) +{ + IMAPMessagePart * attachment; + AbstractPart * subAttachment; + String * nextPartID; + + nextPartID = NULL; + if (message->bd_body->bd_type == MAILIMAP_BODY_1PART) { + // msg or 1part + nextPartID = partID->stringByAppendingUTF8Format(".1"); + } + else if (message->bd_body->bd_type == MAILIMAP_BODY_MPART) { + // mpart + nextPartID = partID; + } + + attachment = new IMAPMessagePart(); + attachment->header()->importIMAPEnvelope(message->bd_envelope); + attachment->importIMAPFields(message->bd_fields, extension); + + subAttachment = attachmentWithIMAPBodyInternal(message->bd_body, nextPartID); + attachment->setMainPart(subAttachment); + + return (IMAPMessagePart *) attachment->autorelease(); +} + +void IMAPPart::importIMAPFields(struct mailimap_body_fields * fields, + struct mailimap_body_ext_1part * extension) +{ + AbstractPart::importIMAPFields(fields, extension); + + setSize(fields->bd_size); + if (fields->bd_encoding != NULL) { + bool isUUEncode; + + isUUEncode = false; + if (fields->bd_encoding->enc_type == MAILIMAP_BODY_FLD_ENC_OTHER) { + if (strcasecmp(fields->bd_encoding->enc_value, "x-uuencode") == 0) { + isUUEncode = true; + } + } + if (isUUEncode) { + setEncoding(EncodingUUEncode); + } + else { + setEncoding((Encoding) fields->bd_encoding->enc_type); + } + } +} + +IMAPPart * IMAPPart::attachmentWithIMAPBody1PartBasic(struct mailimap_body_type_basic * basic, + struct mailimap_body_ext_1part * extension) +{ + IMAPPart * attachment; + String * mimeType; + + attachment = new IMAPPart(); + attachment->importIMAPFields(basic->bd_fields, extension); + + mimeType = NULL; + switch (basic->bd_media_basic->med_type) { + case MAILIMAP_MEDIA_BASIC_APPLICATION: + mimeType = String::stringWithUTF8Format("application/%s", basic->bd_media_basic->med_subtype); + break; + case MAILIMAP_MEDIA_BASIC_AUDIO: + mimeType = String::stringWithUTF8Format("audio/%s", basic->bd_media_basic->med_subtype); + break; + case MAILIMAP_MEDIA_BASIC_IMAGE: + mimeType = String::stringWithUTF8Format("image/%s", basic->bd_media_basic->med_subtype); + break; + case MAILIMAP_MEDIA_BASIC_MESSAGE: + mimeType = String::stringWithUTF8Format("message/%s", basic->bd_media_basic->med_subtype); + break; + case MAILIMAP_MEDIA_BASIC_VIDEO: + mimeType = String::stringWithUTF8Format("video/%s", basic->bd_media_basic->med_subtype); + break; + case MAILIMAP_MEDIA_BASIC_OTHER: + mimeType = String::stringWithUTF8Format("other/%s", basic->bd_media_basic->med_subtype); + break; + } + attachment->setMimeType(mimeType); + + return (IMAPPart *) attachment->autorelease(); +} + +IMAPPart * IMAPPart::attachmentWithIMAPBody1PartText(struct mailimap_body_type_text * text, + struct mailimap_body_ext_1part * extension) +{ + IMAPPart * attachment; + + attachment = new IMAPPart(); + attachment->importIMAPFields(text->bd_fields, extension); + attachment->setMimeType(String::stringWithUTF8Format("text/%s", text->bd_media_text)); + + return (IMAPPart *) attachment->autorelease(); +} + +IMAPMultipart * IMAPPart::attachmentWithIMAPBodyMultipart(struct mailimap_body_type_mpart * body_mpart, + String * partID) +{ + clistiter * cur; + IMAPMultipart * attachment; + unsigned int count; + Array * attachments; + + attachments = new Array(); + + count = 1; + for(cur = clist_begin(body_mpart->bd_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailimap_body * body; + AbstractPart * subResult; + String * nextPartID; + + if (partID == NULL) { + nextPartID = String::stringWithUTF8Format("%u", count); + } + else { + nextPartID = partID->stringByAppendingUTF8Format(".%u", count); + } + body = (struct mailimap_body *) clist_content(cur); + subResult = attachmentWithIMAPBodyInternal(body, nextPartID); + attachments->addObject(subResult); + + count ++; + } + + attachment = new IMAPMultipart(); + if (strcasecmp(body_mpart->bd_media_subtype, "alternative") == 0) { + attachment->setPartType(PartTypeMultipartAlternative); + } + else if (strcasecmp(body_mpart->bd_media_subtype, "related") == 0) { + attachment->setPartType(PartTypeMultipartRelated); + } + attachment->setParts(attachments); + + attachments->release(); + + return (IMAPMultipart *) attachment->autorelease(); +} diff --git a/src/core/imap/MCIMAPPart.h b/src/core/imap/MCIMAPPart.h new file mode 100644 index 00000000..b8b3625d --- /dev/null +++ b/src/core/imap/MCIMAPPart.h @@ -0,0 +1,58 @@ +#ifndef __MAILCORE_MCIMAPPART_H_ + +#define __MAILCORE_MCIMAPPART_H_ + +#include <mailcore/MCBaseTypes.h> +#include <libetpan/libetpan.h> +#include <mailcore/MCAbstractPart.h> + +namespace mailcore { + + class IMAPMessagePart; + class IMAPMultipart; + + class IMAPPart : public AbstractPart { + private: + String * mPartID; + Encoding mEncoding; + unsigned int mSize; + void init(); + static AbstractPart * attachmentWithIMAPBodyInternal(struct mailimap_body * body, String * partID); + static AbstractPart * attachmentWithIMAPBody1Part(struct mailimap_body_type_1part * body_1part, + String * partID); + static IMAPMessagePart * attachmentWithIMAPBody1PartMessage(struct mailimap_body_type_msg * message, + struct mailimap_body_ext_1part * extension, + String * partID); + static IMAPPart * attachmentWithIMAPBody1PartBasic(struct mailimap_body_type_basic * basic, + struct mailimap_body_ext_1part * extension); + static IMAPPart * attachmentWithIMAPBody1PartText(struct mailimap_body_type_text * text, + struct mailimap_body_ext_1part * extension); + static IMAPMultipart * attachmentWithIMAPBodyMultipart(struct mailimap_body_type_mpart * body_mpart, + String * partID); + public: + IMAPPart(); + IMAPPart(IMAPPart * other); + virtual ~IMAPPart(); + + //virtual String * className(); + virtual Object * copy(); + + virtual void setPartID(String * partID); + virtual String * partID(); + + virtual void setSize(unsigned int size); + virtual unsigned int size(); + + virtual void setEncoding(Encoding encoding); + virtual Encoding encoding(); + + unsigned int decodedSize(); + + static AbstractPart * attachmentWithIMAPBody(struct mailimap_body * body); + + virtual void importIMAPFields(struct mailimap_body_fields * fields, + struct mailimap_body_ext_1part * extension); + }; +} + +#endif diff --git a/src/core/imap/MCIMAPProgressCallback.h b/src/core/imap/MCIMAPProgressCallback.h new file mode 100644 index 00000000..b39e0dbd --- /dev/null +++ b/src/core/imap/MCIMAPProgressCallback.h @@ -0,0 +1,16 @@ +#ifndef __MAILCORE_MCIMAPPROGRESSCALLBACK_H_ + +#define __MAILCORE_MCIMAPPROGRESSCALLBACK_H_ + +namespace mailcore { + + class IMAPSession; + + class IMAPProgressCallback { + public: + virtual void bodyProgress(IMAPSession * session, unsigned int current, unsigned int maximum) {}; + virtual void itemsProgress(IMAPSession * session, unsigned int current, unsigned int maximum) {}; + }; +} + +#endif diff --git a/src/core/imap/MCIMAPSearchExpression.cc b/src/core/imap/MCIMAPSearchExpression.cc new file mode 100644 index 00000000..14875c43 --- /dev/null +++ b/src/core/imap/MCIMAPSearchExpression.cc @@ -0,0 +1,163 @@ +#include "MCIMAPSearchExpression.h" + +using namespace mailcore; + +void IMAPSearchExpression::init() +{ + mKind = IMAPSearchKindNone; + mHeader = NULL; + mValue = NULL; + mLeftExpression = NULL; + mRightExpression = NULL; +} + +IMAPSearchExpression::IMAPSearchExpression() +{ + init(); +} + +IMAPSearchExpression::IMAPSearchExpression(IMAPSearchExpression * other) +{ + init(); + mKind = IMAPSearchKindNone; + MC_SAFE_REPLACE_COPY(String, mHeader, other->mHeader); + MC_SAFE_REPLACE_COPY(String, mValue, other->mValue); + MC_SAFE_REPLACE_COPY(IMAPSearchExpression, mLeftExpression, other->mLeftExpression); + MC_SAFE_REPLACE_COPY(IMAPSearchExpression, mRightExpression, other->mRightExpression); +} + +IMAPSearchExpression::~IMAPSearchExpression() +{ + MC_SAFE_RELEASE(mHeader); + MC_SAFE_RELEASE(mValue); + MC_SAFE_RELEASE(mLeftExpression); + MC_SAFE_RELEASE(mRightExpression); +} + +String * IMAPSearchExpression::description() +{ + switch (mKind) { + default: + case IMAPSearchKindNone: + return String::stringWithUTF8Format("<%s:%p None>", MCUTF8(className()), this); + case IMAPSearchKindFrom: + return String::stringWithUTF8Format("<%s:%p From %s>", MCUTF8(className()), this, + MCUTF8(mValue->description())); + case IMAPSearchKindRecipient: + return String::stringWithUTF8Format("<%s:%p Recipient %s>", MCUTF8(className()), this, + MCUTF8(mValue->description())); + case IMAPSearchKindSubject: + return String::stringWithUTF8Format("<%s:%p Subject %s>", MCUTF8(className()), this, + MCUTF8(mValue->description())); + case IMAPSearchKindContent: + return String::stringWithUTF8Format("<%s:%p Content %s>", MCUTF8(className()), this, + MCUTF8(mValue->description())); + case IMAPSearchKindHeader: + return String::stringWithUTF8Format("<%s:%p Header %s %s>", MCUTF8(className()), this, + MCUTF8(mHeader->description()), MCUTF8(mValue->description())); + case IMAPSearchKindOr: + return String::stringWithUTF8Format("<%s:%p Or %s %s>", MCUTF8(className()), this, + MCUTF8(mLeftExpression->description()), MCUTF8(mRightExpression->description())); + case IMAPSearchKindAnd: + return String::stringWithUTF8Format("<%s:%p And %s %s>", MCUTF8(className()), this, + MCUTF8(mLeftExpression->description()), MCUTF8(mRightExpression->description())); + } +} + +#if 0 +String * IMAPSearchExpression::className() +{ + return MCSTR("IMAPSearchExpression"); +} +#endif + +Object * IMAPSearchExpression::copy() +{ + return new IMAPSearchExpression(this); +} + +IMAPSearchExpression * IMAPSearchExpression::searchFrom(String * value) +{ + IMAPSearchExpression * expr = new IMAPSearchExpression(); + expr->mKind = IMAPSearchKindFrom; + MC_SAFE_REPLACE_COPY(String, expr->mValue, value); + return (IMAPSearchExpression *) expr->autorelease(); +} + +IMAPSearchExpression * IMAPSearchExpression::searchRecipient(String * value) +{ + IMAPSearchExpression * expr = new IMAPSearchExpression(); + expr->mKind = IMAPSearchKindRecipient; + MC_SAFE_REPLACE_COPY(String, expr->mValue, value); + return (IMAPSearchExpression *) expr->autorelease(); +} + +IMAPSearchExpression * IMAPSearchExpression::searchSubject(String * value) +{ + IMAPSearchExpression * expr = new IMAPSearchExpression(); + expr->mKind = IMAPSearchKindSubject; + MC_SAFE_REPLACE_COPY(String, expr->mValue, value); + return (IMAPSearchExpression *) expr->autorelease(); +} + +IMAPSearchExpression * IMAPSearchExpression::searchContent(String * value) +{ + IMAPSearchExpression * expr = new IMAPSearchExpression(); + expr->mKind = IMAPSearchKindContent; + MC_SAFE_REPLACE_COPY(String, expr->mValue, value); + return (IMAPSearchExpression *) expr->autorelease(); +} + +IMAPSearchExpression * IMAPSearchExpression::searchHeader(String * header, String * value) +{ + IMAPSearchExpression * expr = new IMAPSearchExpression(); + expr->mKind = IMAPSearchKindHeader; + MC_SAFE_REPLACE_COPY(String, expr->mHeader, header); + MC_SAFE_REPLACE_COPY(String, expr->mValue, value); + return (IMAPSearchExpression *) expr->autorelease(); +} + +IMAPSearchExpression * IMAPSearchExpression::searchAnd(IMAPSearchExpression * left, IMAPSearchExpression * right) +{ + IMAPSearchExpression * expr = new IMAPSearchExpression(); + expr->mKind = IMAPSearchKindAnd; + MC_SAFE_REPLACE_RETAIN(IMAPSearchExpression, expr->mLeftExpression, left); + MC_SAFE_REPLACE_RETAIN(IMAPSearchExpression, expr->mRightExpression, right); + return (IMAPSearchExpression *) expr->autorelease(); +} + +IMAPSearchExpression * IMAPSearchExpression::searchOr(IMAPSearchExpression * left, IMAPSearchExpression * right) +{ + IMAPSearchExpression * expr = new IMAPSearchExpression(); + expr->mKind = IMAPSearchKindOr; + MC_SAFE_REPLACE_RETAIN(IMAPSearchExpression, expr->mLeftExpression, left); + MC_SAFE_REPLACE_RETAIN(IMAPSearchExpression, expr->mRightExpression, right); + return (IMAPSearchExpression *) expr->autorelease(); +} + +IMAPSearchKind IMAPSearchExpression::kind() +{ + return mKind; +} + +String * IMAPSearchExpression::header() +{ + return mHeader; +} + +String * IMAPSearchExpression::value() +{ + return mValue; +} + +IMAPSearchExpression * IMAPSearchExpression::leftExpression() +{ + return mLeftExpression; +} + +IMAPSearchExpression * IMAPSearchExpression::rightExpression() +{ + return mRightExpression; +} + + diff --git a/src/core/imap/MCIMAPSearchExpression.h b/src/core/imap/MCIMAPSearchExpression.h new file mode 100644 index 00000000..53a373cd --- /dev/null +++ b/src/core/imap/MCIMAPSearchExpression.h @@ -0,0 +1,45 @@ +#ifndef __MAILCORE_MCIMAPSEARCHEXPRESSION_H_ + +#define __MAILCORE_MCIMAPSEARCHEXPRESSION_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCMessageConstants.h> + +namespace mailcore { + + class IMAPSearchExpression : Object { + private: + IMAPSearchKind mKind; + String * mHeader; + String * mValue; + IMAPSearchExpression * mLeftExpression; + IMAPSearchExpression * mRightExpression; + void init(); + + public: + IMAPSearchExpression(); + IMAPSearchExpression(IMAPSearchExpression * other); + virtual ~IMAPSearchExpression(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual IMAPSearchKind kind(); + virtual String * header(); + virtual String * value(); + virtual IMAPSearchExpression * leftExpression(); + virtual IMAPSearchExpression * rightExpression(); + + static IMAPSearchExpression * searchFrom(String * value); + static IMAPSearchExpression * searchRecipient(String * value); + static IMAPSearchExpression * searchSubject(String * value); + static IMAPSearchExpression * searchContent(String * value); + static IMAPSearchExpression * searchHeader(String * header, String * value); + static IMAPSearchExpression * searchAnd(IMAPSearchExpression * left, IMAPSearchExpression * right); + static IMAPSearchExpression * searchOr(IMAPSearchExpression * left, IMAPSearchExpression * right); + }; + +} + +#endif diff --git a/src/core/imap/MCIMAPSession.cc b/src/core/imap/MCIMAPSession.cc new file mode 100644 index 00000000..317af507 --- /dev/null +++ b/src/core/imap/MCIMAPSession.cc @@ -0,0 +1,2559 @@ +#include "MCIMAPSession.h" + +#include <string.h> +#include <stdlib.h> + +#include "MCIMAPSearchExpression.h" +#include "MCIMAPFolder.h" +#include "MCIMAPMessage.h" +#include "MCIMAPPart.h" +#include "MCMessageHeader.h" +#include "MCAbstractPart.h" +#include "MCIMAPProgressCallback.h" +#include "MCIMAPNamespace.h" + +using namespace mailcore; + +enum { + STATE_DISCONNECTED, + STATE_CONNECTED, + STATE_LOGGEDIN, + STATE_SELECTED, +}; + +String * mailcore::IMAPNamespacePersonal = NULL; +String * mailcore::IMAPNamespaceOther = NULL; +String * mailcore::IMAPNamespaceShared = NULL; + +__attribute__((constructor)) +static void initialize() { + AutoreleasePool * pool = new AutoreleasePool(); + IMAPNamespacePersonal = (String *) MCSTR("IMAPNamespacePersonal")->retain(); + IMAPNamespaceOther = (String *) MCSTR("IMAPNamespaceOther")->retain(); + IMAPNamespaceShared = (String *) MCSTR("IMAPNamespaceShared")->retain(); + pool->release(); +} + +#define MAX_IDLE_DELAY (28 * 60) + +#define LOCK() pthread_mutex_lock(&mIdleLock) +#define UNLOCK() pthread_mutex_unlock(&mIdleLock) + +static struct mailimap_flag_list * flags_to_lep(MessageFlag value) +{ + struct mailimap_flag_list * flag_list; + + flag_list = mailimap_flag_list_new_empty(); + + if ((value & MessageFlagSeen) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_seen()); + } + + if ((value & MessageFlagFlagged) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flagged()); + } + + if ((value & MessageFlagDeleted) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_deleted()); + } + + if ((value & MessageFlagAnswered) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_answered()); + } + + if ((value & MessageFlagDraft) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_draft()); + } + + if ((value & MessageFlagForwarded) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$Forwarded"))); + } + + if ((value & MessageFlagMDNSent) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$MDNSent"))); + } + + if ((value & MessageFlagSubmitPending) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$SubmitPending"))); + } + + if ((value & MessageFlagSubmitted) != 0) { + mailimap_flag_list_add(flag_list, mailimap_flag_new_flag_keyword(strdup("$Submitted"))); + } + + return flag_list; +} + +static MessageFlag flag_from_lep(struct mailimap_flag * flag) +{ + switch (flag->fl_type) { + case MAILIMAP_FLAG_ANSWERED: + return MessageFlagAnswered; + case MAILIMAP_FLAG_FLAGGED: + return MessageFlagFlagged; + case MAILIMAP_FLAG_DELETED: + return MessageFlagDeleted; + case MAILIMAP_FLAG_SEEN: + return MessageFlagSeen; + case MAILIMAP_FLAG_DRAFT: + return MessageFlagDraft; + case MAILIMAP_FLAG_KEYWORD: + if (strcasecmp(flag->fl_data.fl_keyword, "$Forwarded") == 0) { + return MessageFlagForwarded; + } + else if (strcasecmp(flag->fl_data.fl_keyword, "$MDNSent") == 0) { + return MessageFlagMDNSent; + } + else if (strcasecmp(flag->fl_data.fl_keyword, "$SubmitPending") == 0) { + return MessageFlagSubmitPending; + } + else if (strcasecmp(flag->fl_data.fl_keyword, "$Submitted") == 0) { + return MessageFlagSubmitted; + } + } + + return MessageFlagNone; +} + +static MessageFlag flags_from_lep(struct mailimap_flag_list * flag_list) +{ + MessageFlag flags; + clistiter * iter; + + flags = MessageFlagNone; + for(iter = clist_begin(flag_list->fl_list) ;iter != NULL ; iter = clist_next(iter)) { + struct mailimap_flag * flag; + + flag = (struct mailimap_flag *) clist_content(iter); + flags = (MessageFlag) (flags | flag_from_lep(flag)); + } + + return flags; +} + +static MessageFlag flags_from_lep_att_dynamic(struct mailimap_msg_att_dynamic * att_dynamic) +{ + MessageFlag flags; + clistiter * iter; + + if (att_dynamic->att_list == NULL) + return MessageFlagNone; + + flags = MessageFlagNone; + for(iter = clist_begin(att_dynamic->att_list) ;iter != NULL ; iter = clist_next(iter)) { + struct mailimap_flag_fetch * flag_fetch; + struct mailimap_flag * flag; + + flag_fetch = (struct mailimap_flag_fetch *) clist_content(iter); + if (flag_fetch->fl_type != MAILIMAP_FLAG_FETCH_OTHER) { + continue; + } + + flag = flag_fetch->fl_flag; + flags = (MessageFlag) (flags | flag_from_lep(flag)); + } + + return flags; +} + +#pragma mark set conversion + +static Array * arrayFromSet(struct mailimap_set * imap_set) +{ + Array * result; + clistiter * iter; + + result = Array::array(); + for(iter = clist_begin(imap_set->set_list) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set_item * item; + unsigned long i; + + item = (struct mailimap_set_item *) clist_content(iter); + for(i = item->set_first ; i <= item->set_last ; i ++) { + Value * nb; + + nb = Value::valueWithUnsignedLongValue(i); + result->addObject(nb); + } + } + + return result; +} + +static int compareValuesUnsignedLong(void * value1, void * value2, void * context) +{ + Value * concreteValue1 = (Value *) value1; + Value * concreteValue2 = (Value *) value2; + + if (concreteValue2->unsignedLongValue() > concreteValue1->unsignedLongValue()) { + return 1; + } + else if (concreteValue2->unsignedLongValue() < concreteValue1->unsignedLongValue()) { + return -1; + } + else { + return 0; + } +} + +static struct mailimap_set * setFromArray(Array * array) +{ + unsigned int currentIndex; + unsigned int currentFirst; + unsigned int currentValue; + unsigned int lastValue; + struct mailimap_set * imap_set; + + currentFirst = 0; + currentValue = 0; + lastValue = 0; + currentIndex = 0; + + array = array->sortedArray(compareValuesUnsignedLong, NULL); + imap_set = mailimap_set_new_empty(); + + while (currentIndex < array->count()) { + currentValue = (unsigned int) ((Value *) array->objectAtIndex(currentIndex))->unsignedLongValue(); + if (currentFirst == 0) { + currentFirst = currentValue; + } + + if ((lastValue != 0) && (currentValue != lastValue + 1)) { + mailimap_set_add_interval(imap_set, currentFirst, lastValue); + currentFirst = 0; + lastValue = 0; + } + else { + lastValue = currentValue; + currentIndex ++; + } + } + if (currentFirst != 0) { + mailimap_set_add_interval(imap_set, currentFirst, lastValue); + } + + return imap_set; +} + +static clist * splitSet(struct mailimap_set * set, unsigned int splitCount) +{ + struct mailimap_set * current_set; + clist * result; + unsigned int count; + + result = clist_new(); + + current_set = NULL; + count = 0; + for(clistiter * iter = clist_begin(set->set_list) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set_item * item; + + if (current_set == NULL) { + current_set = mailimap_set_new_empty(); + } + + item = (struct mailimap_set_item *) clist_content(iter); + mailimap_set_add_interval(current_set, item->set_first, item->set_last); + count ++; + + if (count >= splitCount) { + clist_append(result, current_set); + current_set = NULL; + count = 0; + } + } + if (current_set != NULL) { + clist_append(result, current_set); + } + + return result; +} + +void IMAPSession::init() +{ + mHostname = NULL; + mPort = 0; + mUsername = NULL; + mPassword = NULL; + mAuthType = AuthTypeSASLNone; + mConnectionType = ConnectionTypeClear; + mCheckCertificateEnabled = true; + mVoIPEnabled = true; + mDelimiter = 0; + + mIdleEnabled = false; + mXListEnabled = false; + mWelcomeString = NULL; + mNeedsMboxMailWorkaround = false; + mDefaultNamespace = NULL; + mTimeout = 30; + mUIDValidity = 0; + mUIDNext = 0; + mFolderMsgCount = 0; + mLastFetchedSequenceNumber = 0; + mCurrentFolder = NULL; + pthread_mutex_init(&mIdleLock, NULL); + mState = STATE_DISCONNECTED; + mImap = NULL; + mProgressCallback = NULL; + mProgressItemsCount = 0; +} + +IMAPSession::IMAPSession() +{ + init(); +} + +IMAPSession::~IMAPSession() +{ + MC_SAFE_RELEASE(mHostname); + MC_SAFE_RELEASE(mUsername); + MC_SAFE_RELEASE(mPassword); + MC_SAFE_RELEASE(mWelcomeString); + MC_SAFE_RELEASE(mDefaultNamespace); + MC_SAFE_RELEASE(mCurrentFolder); + pthread_mutex_destroy(&mIdleLock); +} + +#if 0 +String * IMAPSession::className() +{ + return MCSTR("IMAPSession"); +} +#endif + +void IMAPSession::setHostname(String * hostname) +{ + MC_SAFE_REPLACE_COPY(String, mHostname, hostname); +} + +String * IMAPSession::hostname() +{ + return mHostname; +} + +void IMAPSession::setPort(unsigned int port) +{ + mPort = port; +} + +unsigned int IMAPSession::port() +{ + return mPort; +} + +void IMAPSession::setUsername(String * username) +{ + MC_SAFE_REPLACE_COPY(String, mUsername, username); +} + +String * IMAPSession::username() +{ + return mUsername; +} + +void IMAPSession::setPassword(String * password) +{ + MC_SAFE_REPLACE_COPY(String, mPassword, password); +} + +String * IMAPSession::password() +{ + return mPassword; +} + +void IMAPSession::setAuthType(AuthType authType) +{ + mAuthType = authType; +} + +AuthType IMAPSession::authType() +{ + return mAuthType; +} + +void IMAPSession::setConnectionType(ConnectionType connectionType) +{ + mConnectionType = connectionType; +} + +ConnectionType IMAPSession::connectionType() +{ + return mConnectionType; +} + +void IMAPSession::setTimeout(time_t timeout) +{ + mTimeout = timeout; +} + +time_t IMAPSession::timeout() +{ + return mTimeout; +} + +void IMAPSession::setCheckCertificateEnabled(bool enabled) +{ + mCheckCertificateEnabled = enabled; +} + +bool IMAPSession::isCheckCertificateEnabled() +{ + return mCheckCertificateEnabled; +} + +void IMAPSession::setVoIPEnabled(bool enabled) +{ + mVoIPEnabled = enabled; +} + +bool IMAPSession::isVoIPEnabled() +{ + return mVoIPEnabled; +} + + +void IMAPSession::setDelimiter(char delimiter) +{ + mDelimiter = delimiter; +} + +char IMAPSession::delimiter() +{ + return mDelimiter; +} + +static bool hasError(int errorCode) +{ + return ((errorCode != MAILIMAP_NO_ERROR) && (errorCode != MAILIMAP_NO_ERROR_AUTHENTICATED) && + (errorCode != MAILIMAP_NO_ERROR_NON_AUTHENTICATED)); +} + +bool IMAPSession::checkCertificate() +{ + // XXX + return true; +} + +void IMAPSession::body_progress(size_t current, size_t maximum, void * context) +{ + IMAPSession * session; + + session = (IMAPSession *) context; + session->bodyProgress((unsigned int) current, (unsigned int) maximum); +} + +void IMAPSession::items_progress(size_t current, size_t maximum, void * context) +{ + IMAPSession * session; + + session = (IMAPSession *) context; + session->itemsProgress((unsigned int) current, (unsigned int) maximum); +} + +void IMAPSession::setup() +{ + MCAssert(mImap == NULL); + + mImap = mailimap_new(0, NULL); + mailimap_set_timeout(mImap, timeout()); + mailimap_set_progress_callback(mImap, body_progress, IMAPSession::items_progress, this); +} + +void IMAPSession::unsetup() +{ + mailimap * imap; + + imap = mImap; + mImap = NULL; + + if (imap != NULL) { + if (imap->imap_stream != NULL) { + mailstream_close(imap->imap_stream); + imap->imap_stream = NULL; + } + mailimap_free(imap); + imap = NULL; + } +} + +void IMAPSession::connect(ErrorCode * pError) +{ + int r; + + setup(); + + MCLog("connect %s", MCUTF8DESC(this)); + + MCAssert(mState == STATE_DISCONNECTED); + + switch (mConnectionType) { + case ConnectionTypeStartTLS: + MCLog("STARTTLS connect"); + r = mailimap_socket_connect_voip(mImap, MCUTF8(mHostname), mPort, isVoIPEnabled()); + if (hasError(r)) { + * pError = ErrorConnection; + return; + } + + r = mailimap_socket_starttls(mImap); + if (hasError(r)) { + MCLog("no TLS %i", r); + * pError = ErrorTLSNotAvailable; + return; + } + break; + + case ConnectionTypeTLS: + r = mailimap_ssl_connect_voip(mImap, MCUTF8(mHostname), mPort, isVoIPEnabled()); + MCLog("ssl connect %s %u %u", MCUTF8(mHostname), mPort, r); + if (hasError(r)) { + MCLog("connect error %i", r); + * pError = ErrorConnection; + return; + } + if (!checkCertificate()) { + MCLog("ssl connect certificate ERROR %d", r); + * pError = ErrorCertificate; + return; + } + + break; + + default: + MCLog("socket connect %s %u", MCUTF8(mHostname), mPort); + r = mailimap_socket_connect_voip(mImap, MCUTF8(mHostname), mPort, isVoIPEnabled()); + MCLog("socket connect %i", r); + if (hasError(r)) { + MCLog("connect error %i", r); + * pError = ErrorConnection; + return; + } + break; + } + + mailstream_low * low; + String * identifierString; + char * identifier; + + low = mailstream_get_low(mImap->imap_stream); + identifierString = String::stringWithUTF8Format("%s@%s:%u", MCUTF8(mUsername), MCUTF8(mHostname), mPort); + identifier = strdup(identifierString->UTF8Characters()); + mailstream_low_set_identifier(low, identifier); + + if (mImap->imap_response != NULL) { + MC_SAFE_REPLACE_RETAIN(String, mWelcomeString, String::stringWithUTF8Characters(mImap->imap_response)); + } + + * pError = ErrorNone; + mState = STATE_CONNECTED; + MCLog("connect ok"); +} + +void IMAPSession::connectIfNeeded(ErrorCode * pError) +{ + if (mState == STATE_DISCONNECTED) { + connect(pError); + } + else { + * pError = ErrorNone; + } +} + +void IMAPSession::loginIfNeeded(ErrorCode * pError) +{ + connectIfNeeded(pError); + if (* pError != ErrorNone) + return; + + if (mState == STATE_CONNECTED) { + login(pError); + } + else { + * pError = ErrorNone; + } +} + +void IMAPSession::login(ErrorCode * pError) +{ + int r; + + MCLog("login"); + + MCAssert(mState == STATE_CONNECTED); + + const char* utf8username; + const char* utf8password; + utf8username = MCUTF8(mUsername); + utf8password = MCUTF8(mPassword); + if (utf8username == NULL) { + utf8username = ""; + } + if (utf8password == NULL) { + utf8password = ""; + } + + switch (mAuthType) { + case 0: + default: + r = mailimap_login(mImap, utf8username, utf8password); + break; + + case AuthTypeSASLCRAMMD5: + r = mailimap_authenticate(mImap, "CRAM-MD5", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLPlain: + r = mailimap_authenticate(mImap, "PLAIN", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLGSSAPI: + // needs to be tested + r = mailimap_authenticate(mImap, "GSSAPI", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL /* realm */); + break; + + case AuthTypeSASLDIGESTMD5: + r = mailimap_authenticate(mImap, "DIGEST-MD5", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLLogin: + r = mailimap_authenticate(mImap, "LOGIN", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLSRP: + r = mailimap_authenticate(mImap, "SRP", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLNTLM: + r = mailimap_authenticate(mImap, "NTLM", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL/* realm */); + break; + + case AuthTypeSASLKerberosV4: + r = mailimap_authenticate(mImap, "KERBEROS_V4", + MCUTF8(mHostname), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL/* realm */); + break; + } + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + String * response; + + response = MCSTR(""); + if (mImap->imap_response != NULL) { + response = String::stringWithUTF8Characters(mImap->imap_response); + } + if (response->locationOfString(MCSTR("not enabled for IMAP use")) != -1) { + * pError = ErrorGmailIMAPNotEnabled; + } + else if (response->locationOfString(MCSTR("bandwidth limits")) != -1) { + * pError = ErrorGmailExceededBandwidthLimit; + } + else if (response->locationOfString(MCSTR("Too many simultaneous connections")) != -1) { + * pError = ErrorGmailTooManySimultaneousConnections; + } + else if (response->locationOfString(MCSTR("Maximum number of connections")) != -1) { + * pError = ErrorGmailTooManySimultaneousConnections; + } + else if (response->locationOfString(MCSTR("http://me.com/move")) != -1) { + * pError = ErrorMobileMeMoved; + } + else if (response->locationOfString(MCSTR("OCF12")) != -1) { + * pError = ErrorYahooUnavailable; + } + else { + * pError = ErrorAuthentication; + } + return; + } + + * pError = ErrorNone; + mState = STATE_LOGGEDIN; + MCLog("login ok"); +} + +void IMAPSession::selectIfNeeded(String * folder, ErrorCode * pError) +{ + loginIfNeeded(pError); + if (* pError != ErrorNone) + return; + + if (mState == STATE_LOGGEDIN) { + select(folder, pError); + } + else { + * pError = ErrorNone; + } +} + +void IMAPSession::select(String * folder, ErrorCode * pError) +{ + int r; + + MCLog("select"); + MCAssert(mState == STATE_LOGGEDIN || mState == STATE_SELECTED); + + r = mailimap_select(mImap, MCUTF8(folder)); + MCLog("select error : %i", r); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + MCLog("select error : %s %i", MCUTF8DESC(this), * pError); + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorNonExistantFolder; + MC_SAFE_RELEASE(mCurrentFolder); + return; + } + + MC_SAFE_REPLACE_COPY(String, mCurrentFolder, folder); + + if (mImap->imap_selection_info != NULL) { + mUIDValidity = mImap->imap_selection_info->sel_uidvalidity; + mUIDNext = mImap->imap_selection_info->sel_uidnext; + if (mImap->imap_selection_info->sel_has_exists) { + mFolderMsgCount = (unsigned int) (mImap->imap_selection_info->sel_exists); + } else { + mFolderMsgCount = -1; + } + } + + mState = STATE_SELECTED; + * pError = ErrorNone; + MCLog("select ok"); +} + +#pragma mark mailbox flags conversion + +static struct { + const char * name; + int flag; +} mb_keyword_flag[] = { + {"Inbox", IMAPFolderFlagInbox}, + {"AllMail", IMAPFolderFlagAllMail}, + {"Sent", IMAPFolderFlagSentMail}, + {"Spam", IMAPFolderFlagSpam}, + {"Starred", IMAPFolderFlagStarred}, + {"Trash", IMAPFolderFlagTrash}, + {"Important", IMAPFolderFlagImportant}, + {"Drafts", IMAPFolderFlagDrafts}, + {"Archive", IMAPFolderFlagArchive}, +}; + +static int imap_mailbox_flags_to_flags(struct mailimap_mbx_list_flags * imap_flags) +{ + int flags; + clistiter * cur; + + flags = 0; + if (imap_flags->mbf_type == MAILIMAP_MBX_LIST_FLAGS_SFLAG) { + switch (imap_flags->mbf_sflag) { + case MAILIMAP_MBX_LIST_SFLAG_MARKED: + flags |= IMAPFolderFlagMarked; + break; + case MAILIMAP_MBX_LIST_SFLAG_NOSELECT: + flags |= IMAPFolderFlagNoSelect; + break; + case MAILIMAP_MBX_LIST_SFLAG_UNMARKED: + flags |= IMAPFolderFlagUnmarked; + break; + } + } + + for(cur = clist_begin(imap_flags->mbf_oflags) ; cur != NULL ; + cur = clist_next(cur)) { + struct mailimap_mbx_list_oflag * oflag; + + oflag = (struct mailimap_mbx_list_oflag *) clist_content(cur); + + switch (oflag->of_type) { + case MAILIMAP_MBX_LIST_OFLAG_NOINFERIORS: + flags |= IMAPFolderFlagNoInferiors; + break; + + case MAILIMAP_MBX_LIST_OFLAG_FLAG_EXT: + for(unsigned int i = 0 ; i < sizeof(mb_keyword_flag) / sizeof(mb_keyword_flag[0]) ; i ++) { + if (strcasecmp(mb_keyword_flag[i].name, oflag->of_flag_ext) == 0) { + flags |= mb_keyword_flag[i].flag; + } + } + break; + } + } + + return flags; +} + +static Array * resultsWithError(int r, clist * list, ErrorCode * pError) +{ + clistiter * cur; + Array * result; + + result = Array::array(); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorNonExistantFolder; + return NULL; + } + + for(cur = clist_begin(list) ; cur != NULL ; cur = cur->next) { + struct mailimap_mailbox_list * mb_list; + IMAPFolderFlag flags; + IMAPFolder * folder; + String * path; + + mb_list = (struct mailimap_mailbox_list *) cur->data; + + flags = IMAPFolderFlagNone; + if (mb_list->mb_flag != NULL) + flags = (IMAPFolderFlag) imap_mailbox_flags_to_flags(mb_list->mb_flag); + + folder = new IMAPFolder(); + path = String::stringWithUTF8Characters(mb_list->mb_name); + if (path->uppercaseString()->isEqual(MCSTR("INBOX"))) { + folder->setPath(MCSTR("INBOX")); + } + else { + folder->setPath(path); + } + folder->setDelimiter(mb_list->mb_delimiter); + folder->setFlags(flags); + + result->addObject(folder); + + folder->release(); + } + + mailimap_list_result_free(list); + + * pError = ErrorNone; + return result; +} + +char IMAPSession::fetchDelimiterIfNeeded(char defaultDelimiter, ErrorCode * pError) +{ + int r; + clist * imap_folders; + IMAPFolder * folder; + Array * folders; + + if (defaultDelimiter != 0) + return defaultDelimiter; + + r = mailimap_list(mImap, "", "", &imap_folders); + folders = resultsWithError(r, imap_folders, pError); + if (* pError != ErrorNone) + return 0; + + if (folders->count() > 0) { + folder = (IMAPFolder *) folders->objectAtIndex(0); + } + else { + folder = NULL; + } + if (folder == NULL) + return 0; + + * pError = ErrorNone; + return folder->delimiter(); +} + +Array * /* IMAPFolder */ IMAPSession::fetchSubscribedFolders(ErrorCode * pError) +{ + int r; + clist * imap_folders; + char delimiter; + + MCLog("fetch subscribed"); + loginIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + delimiter = fetchDelimiterIfNeeded(mDelimiter, pError); + if (* pError != ErrorNone) + return NULL; + + setDelimiter(delimiter); + + String * prefix; + prefix = defaultNamespace()->mainPrefix(); + if (prefix == NULL) { + prefix = MCSTR(""); + } + if (prefix->length() > 0) { + if (!prefix->hasSuffix(String::stringWithUTF8Format("%c", delimiter))) { + prefix = prefix->stringByAppendingUTF8Format("%c", delimiter); + } + } + + r = mailimap_lsub(mImap, MCUTF8(prefix), "*", &imap_folders); + MCLog("fetch subscribed %u", r); + return resultsWithError(r, imap_folders, pError); +} + +Array * /* IMAPFolder */ IMAPSession::fetchAllFolders(ErrorCode * pError) +{ + int r; + clist * imap_folders; + char delimiter; + + loginIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + delimiter = fetchDelimiterIfNeeded(mDelimiter, pError); + if (* pError != ErrorNone) + return NULL; + + setDelimiter(delimiter); + + String * prefix; + prefix = defaultNamespace()->mainPrefix(); + if (prefix == NULL) { + prefix = MCSTR(""); + } + if (prefix->length() > 0) { + if (!prefix->hasSuffix(String::stringWithUTF8Format("%c", delimiter))) { + prefix = prefix->stringByAppendingUTF8Format("%c", delimiter); + } + } + + if (mXListEnabled) { + r = mailimap_xlist(mImap, MCUTF8(prefix), "*", &imap_folders); + } + else { + r = mailimap_list(mImap, MCUTF8(prefix), "*", &imap_folders); + } + return resultsWithError(r, imap_folders, pError); +} + +void IMAPSession::renameFolder(String * folder, String * otherName, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_rename(mImap, MCUTF8(folder), MCUTF8(otherName)); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorRename; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::deleteFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_delete(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorDelete; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::createFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_create(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorCreate; + return; + } + + * pError = ErrorNone; + subscribeFolder(folder, pError); +} + +void IMAPSession::subscribeFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_subscribe(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorSubscribe; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::unsubscribeFolder(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(MCSTR("INBOX"), pError); + if (* pError != ErrorNone) + return; + + r = mailimap_unsubscribe(mImap, MCUTF8(folder)); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorSubscribe; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::appendMessage(String * folder, Data * messageData, MessageFlag flags, + IMAPProgressCallback * progressCallback, uint32_t * createdUID, ErrorCode * pError) +{ + int r; + struct mailimap_flag_list * flag_list; + uint32_t uidvalidity; + uint32_t uidresult; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + mProgressCallback = progressCallback; + bodyProgress(0, messageData->length()); + + flag_list = NULL; + flag_list = flags_to_lep(flags); + r = mailimap_uidplus_append(mImap, MCUTF8(folder), flag_list, NULL, messageData->bytes(), messageData->length(), + &uidvalidity, &uidresult); + mailimap_flag_list_free(flag_list); + + bodyProgress(messageData->length(), messageData->length()); + mProgressCallback = NULL; + + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorAppend; + return; + } + + * createdUID = uidresult; + * pError = ErrorNone; +} + +void IMAPSession::copyMessages(String * folder, Array * uidSet, String * destFolder, + Array ** pDestUIDs, ErrorCode * pError) +{ + int r; + struct mailimap_set * set; + struct mailimap_set * src_uid; + struct mailimap_set * dest_uid; + uint32_t uidvalidity; + clist * setList; + Array * uidSetResult; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + set = setFromArray(uidSet); + if (clist_count(set->set_list) == 0) { + return; + } + + setList = splitSet(set, 10); + uidSetResult = new Array(); + + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + + r = mailimap_uidplus_uid_copy(mImap, current_set, MCUTF8(destFolder), + &uidvalidity, &src_uid, &dest_uid); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + goto release; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + goto release; + } + else if (hasError(r)) { + * pError = ErrorCopy; + goto release; + } + + if (src_uid != NULL) { + mailimap_set_free(src_uid); + } + + if (dest_uid != NULL) { + uidSetResult->addObjectsFromArray(arrayFromSet(dest_uid)); + mailimap_set_free(dest_uid); + } + } + * pDestUIDs = (Array *) uidSetResult->retain()->autorelease(); + * pError = ErrorNone; + + release: + MC_SAFE_RELEASE(uidSetResult); + + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + mailimap_set_free(current_set); + } + clist_free(setList); + mailimap_set_free(set); +} + +void IMAPSession::expunge(String * folder, ErrorCode * pError) +{ + int r; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + r = mailimap_expunge(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorExpunge; + return; + } + * pError = ErrorNone; +} + +static int +fetch_imap(mailimap * imap, uint32_t uid, + struct mailimap_fetch_type * fetch_type, + char ** result, size_t * result_len) +{ + int r; + struct mailimap_msg_att * msg_att; + struct mailimap_msg_att_item * msg_att_item; + clist * fetch_result; + struct mailimap_set * set; + char * text; + size_t text_length; + clistiter * cur; + + set = mailimap_set_new_single(uid); + r = mailimap_uid_fetch(imap, set, fetch_type, &fetch_result); + + mailimap_set_free(set); + + switch (r) { + case MAILIMAP_NO_ERROR: + break; + default: + return r; + } + + if (clist_begin(fetch_result) == NULL) { + mailimap_fetch_list_free(fetch_result); + return MAILIMAP_ERROR_FETCH; + } + + msg_att = (struct mailimap_msg_att *) clist_begin(fetch_result)->data; + + text = NULL; + text_length = 0; + + for(cur = clist_begin(msg_att->att_list) ; cur != NULL ; + cur = clist_next(cur)) { + msg_att_item = (struct mailimap_msg_att_item *) clist_content(cur); + + if (msg_att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) { + + if (msg_att_item->att_data.att_static->att_type == + MAILIMAP_MSG_ATT_BODY_SECTION) { + text = msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part; + msg_att_item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL; + text_length = + msg_att_item->att_data.att_static->att_data.att_body_section->sec_length; + } + } + } + + mailimap_fetch_list_free(fetch_result); + + if (text == NULL) + return MAILIMAP_ERROR_FETCH; + + * result = text; + * result_len = text_length; + + return MAILIMAP_NO_ERROR; +} + +HashMap * IMAPSession::fetchMessageNumberUIDMapping(String * folder, uint32_t fromUID, uint32_t toUID, + ErrorCode * pError) +{ + struct mailimap_set * imap_set; + struct mailimap_fetch_type * fetch_type; + clist * fetch_result; + HashMap * result; + struct mailimap_fetch_att * fetch_att; + int r; + clistiter * iter; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + result = HashMap::hashMap(); + + imap_set = mailimap_set_new_interval(fromUID, toUID); + fetch_type = mailimap_fetch_type_new_fetch_att_list_empty(); + fetch_att = mailimap_fetch_att_new_uid(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + + r = mailimap_uid_fetch(mImap, imap_set, fetch_type, &fetch_result); + mailimap_fetch_type_free(fetch_type); + mailimap_set_free(imap_set); + + if (r == MAILIMAP_ERROR_STREAM) { + MCLog("error stream"); + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + MCLog("error parse"); + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + MCLog("error fetch"); + * pError = ErrorFetch; + return NULL; + } + + for(iter = clist_begin(fetch_result) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_msg_att * msg_att; + clistiter * item_iter; + uint32_t uid; + + msg_att = (struct mailimap_msg_att *) clist_content(iter); + uid = 0; + for(item_iter = clist_begin(msg_att->att_list) ; item_iter != NULL ; item_iter = clist_next(item_iter)) { + struct mailimap_msg_att_item * att_item; + + att_item = (struct mailimap_msg_att_item *) clist_content(item_iter); + if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) { + struct mailimap_msg_att_static * att_static; + + att_static = att_item->att_data.att_static; + if (att_static->att_type == MAILIMAP_MSG_ATT_UID) { + uid = att_static->att_data.att_uid; + } + } + } + + if (uid < fromUID) { + uid = 0; + } + + if (uid != 0) { + result->setObjectForKey(Value::valueWithUnsignedLongValue(msg_att->att_number), + Value::valueWithUnsignedLongValue(uid)); + } + } + + mailimap_fetch_list_free(fetch_result); + * pError = ErrorNone; + + return result; +} + +struct msg_att_handler_data { + IMAPSession * self; + bool fetchByUID; + Array * result; + String * folder; + IMAPMessagesRequestKind requestKind; + uint32_t mLastFetchedSequenceNumber; + HashMap * mapping; + bool needsHeader; + bool needsBody; + bool needsFlags; + bool needsGmailLabels; + uint32_t startUid; +}; + +static void msg_att_handler(struct mailimap_msg_att * msg_att, void * context) +{ + clistiter * item_iter; + uint32_t uid; + IMAPMessage * msg; + bool hasHeader; + bool hasBody; + bool hasFlags; + bool hasGmailLabels; + struct msg_att_handler_data * msg_att_context; + // struct + IMAPSession * self; + bool fetchByUID; + Array * result; + String * folder; + IMAPMessagesRequestKind requestKind; + uint32_t mLastFetchedSequenceNumber; + HashMap * mapping; + bool needsHeader; + bool needsBody; + bool needsFlags; + bool needsGmailLabels; + uint32_t startUid; + + msg_att_context = (struct msg_att_handler_data *) context; + self = msg_att_context->self; + fetchByUID = msg_att_context->fetchByUID; + result = msg_att_context->result; + folder = msg_att_context->folder; + requestKind = msg_att_context->requestKind; + mLastFetchedSequenceNumber = msg_att_context->mLastFetchedSequenceNumber; + mapping = msg_att_context->mapping; + needsHeader = msg_att_context->needsHeader; + needsBody = msg_att_context->needsBody; + needsFlags = msg_att_context->needsFlags; + needsGmailLabels = msg_att_context->needsGmailLabels; + startUid = msg_att_context->startUid; + + hasHeader = false; + hasBody = false; + hasFlags = false; + hasGmailLabels = false; + + msg = new IMAPMessage(); + + uid = 0; + mLastFetchedSequenceNumber = msg_att->att_number; + if (mapping != NULL) { + uid = (uint32_t) ((Value *) mapping->objectForKey(Value::valueWithUnsignedLongValue(msg_att->att_number)))->longLongValue(); + } + for(item_iter = clist_begin(msg_att->att_list) ; item_iter != NULL ; item_iter = clist_next(item_iter)) { + struct mailimap_msg_att_item * att_item; + + att_item = (struct mailimap_msg_att_item *) clist_content(item_iter); + if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_DYNAMIC) { + MessageFlag flags; + + flags = flags_from_lep_att_dynamic(att_item->att_data.att_dyn); + msg->setFlags(flags); + msg->setOriginalFlags(flags); + hasFlags = true; + } + else if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) { + struct mailimap_msg_att_static * att_static; + + att_static = att_item->att_data.att_static; + if (att_static->att_type == MAILIMAP_MSG_ATT_UID) { + uid = att_static->att_data.att_uid; + } + else if (att_static->att_type == MAILIMAP_MSG_ATT_ENVELOPE) { + struct mailimap_envelope * env; + + MCLog("parse envelope %lu", (unsigned long) uid); + env = att_static->att_data.att_env; + msg->header()->importIMAPEnvelope(env); + hasHeader = true; + } + else if (att_static->att_type == MAILIMAP_MSG_ATT_BODY_SECTION) { + if ((requestKind & IMAPMessagesRequestKindFullHeaders) != 0) { + char * bytes; + size_t length; + + bytes = att_static->att_data.att_body_section->sec_body_part; + length = att_static->att_data.att_body_section->sec_length; + + msg->header()->importHeadersData(Data::dataWithBytes(bytes, (unsigned int) length)); + hasHeader = true; + } + else { + char * references; + size_t ref_size; + + // references + references = att_static->att_data.att_body_section->sec_body_part; + ref_size = att_static->att_data.att_body_section->sec_length; + + msg->header()->importIMAPReferences(Data::dataWithBytes(references, (unsigned int) ref_size)); + } + } + else if (att_static->att_type == MAILIMAP_MSG_ATT_BODYSTRUCTURE) { + AbstractPart * mainPart; + + // bodystructure + mainPart = IMAPPart::attachmentWithIMAPBody(att_static->att_data.att_body); + msg->setMainPart(mainPart); + hasBody = true; + } + } + else if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_EXTENSION) { + struct mailimap_extension_data * ext_data; + + ext_data = att_item->att_data.att_extension_data; + if (ext_data->ext_extension == &mailimap_extension_xgmlabels) { + struct mailimap_msg_att_xgmlabels * cLabels; + Array * labels; + clistiter * cur; + + labels = new Array(); + hasGmailLabels = true; + cLabels = (struct mailimap_msg_att_xgmlabels *) ext_data->ext_data; + for(cur = clist_begin(cLabels->att_labels) ; cur != NULL ; cur = clist_next(cur)) { + char * cLabel; + String * label; + + cLabel = (char *) clist_content(cur); + label = String::stringWithUTF8Characters(cLabel); + labels->addObject(label); + } + if (labels->count() > 0) { + msg->setGmailLabels(labels); + } + labels->release(); + } + } + } + for(item_iter = clist_begin(msg_att->att_list) ; item_iter != NULL ; item_iter = clist_next(item_iter)) { + struct mailimap_msg_att_item * att_item; + + att_item = (struct mailimap_msg_att_item *) clist_content(item_iter); + if (att_item->att_type == MAILIMAP_MSG_ATT_ITEM_STATIC) { + struct mailimap_msg_att_static * att_static; + + att_static = att_item->att_data.att_static; + if (att_static->att_type == MAILIMAP_MSG_ATT_INTERNALDATE) { + msg->header()->importIMAPInternalDate(att_static->att_data.att_internal_date); + } + } + } + + if (fetchByUID) { + if (uid < startUid) { + uid = 0; + } + } + + if (needsBody && !hasBody) { + msg->release(); + return; + } + if (needsHeader && !hasHeader) { + msg->release(); + return; + } + if (needsFlags && !hasFlags) { + msg->release(); + return; + } + + if (uid != 0) { + msg->setUid(uid); + } + else { + msg->release(); + return; + } + + result->addObject(msg); + msg->release(); + + msg_att_context->mLastFetchedSequenceNumber = mLastFetchedSequenceNumber; +} + +Array * IMAPSession::fetchMessages(String * folder, IMAPMessagesRequestKind requestKind, bool fetchByUID, + struct mailimap_set * imapset, HashMap * mapping, uint32_t startUid, + IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + struct mailimap_fetch_type * fetch_type; + clist * fetch_result; + Array * result; + struct mailimap_fetch_att * fetch_att; + int r; + bool needsHeader; + bool needsBody; + bool needsFlags; + bool needsGmailLabels; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + if (mNeedsMboxMailWorkaround) { + requestKind = (IMAPMessagesRequestKind) (requestKind & ~IMAPMessagesRequestKindHeaders); + requestKind = (IMAPMessagesRequestKind) (requestKind | IMAPMessagesRequestKindFullHeaders); + } + + if ((requestKind & IMAPMessagesRequestKindHeaders) != 0) { + mProgressItemsCount = 0; + mProgressCallback = progressCallback; + } + + result = Array::array(); + + needsHeader = false; + needsBody = false; + needsFlags = false; + needsGmailLabels = false; + + fetch_type = mailimap_fetch_type_new_fetch_att_list_empty(); + fetch_att = mailimap_fetch_att_new_uid(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + if ((requestKind & IMAPMessagesRequestKindFlags) != 0) { + MCLog("request flags"); + fetch_att = mailimap_fetch_att_new_flags(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsFlags = true; + } + if ((requestKind & IMAPMessagesRequestKindGmailLabels) != 0) { + MCLog("request flags"); + fetch_att = mailimap_fetch_att_new_xgmlabels(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsGmailLabels = true; + } + if ((requestKind & IMAPMessagesRequestKindFullHeaders) != 0) { + clist * hdrlist; + char * header; + struct mailimap_header_list * imap_hdrlist; + struct mailimap_section * section; + + MCLog("request envelope"); + + // most important header + hdrlist = clist_new(); + header = strdup("Date"); + clist_append(hdrlist, header); + header = strdup("Subject"); + clist_append(hdrlist, header); + header = strdup("From"); + clist_append(hdrlist, header); + header = strdup("Sender"); + clist_append(hdrlist, header); + header = strdup("Reply-To"); + clist_append(hdrlist, header); + header = strdup("To"); + clist_append(hdrlist, header); + header = strdup("Cc"); + clist_append(hdrlist, header); + header = strdup("Message-ID"); + clist_append(hdrlist, header); + header = strdup("References"); + clist_append(hdrlist, header); + header = strdup("In-Reply-To"); + clist_append(hdrlist, header); + imap_hdrlist = mailimap_header_list_new(hdrlist); + section = mailimap_section_new_header_fields(imap_hdrlist); + fetch_att = mailimap_fetch_att_new_body_peek_section(section); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsHeader = true; + } + if ((requestKind & IMAPMessagesRequestKindHeaders) != 0) { + clist * hdrlist; + char * header; + struct mailimap_header_list * imap_hdrlist; + struct mailimap_section * section; + + MCLog("request envelope"); + // envelope + fetch_att = mailimap_fetch_att_new_envelope(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + + // references header + hdrlist = clist_new(); + header = strdup("References"); + clist_append(hdrlist, header); + if ((requestKind & IMAPMessagesRequestKindHeaderSubject) != 0) { + header = strdup("Subject"); + clist_append(hdrlist, header); + } + imap_hdrlist = mailimap_header_list_new(hdrlist); + section = mailimap_section_new_header_fields(imap_hdrlist); + fetch_att = mailimap_fetch_att_new_body_peek_section(section); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsHeader = true; + } + if ((requestKind & IMAPMessagesRequestKindStructure) != 0) { + // message structure + MCLog("request bodystructure"); + fetch_att = mailimap_fetch_att_new_bodystructure(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + needsBody = true; + } + if ((requestKind & IMAPMessagesRequestKindInternalDate) != 0) { + // internal date + fetch_att = mailimap_fetch_att_new_internaldate(); + mailimap_fetch_type_new_fetch_att_list_add(fetch_type, fetch_att); + } + + struct msg_att_handler_data msg_att_data; + + memset(&msg_att_data, 0, sizeof(msg_att_data)); + msg_att_data.self = this; + msg_att_data.fetchByUID = fetchByUID; + msg_att_data.result = result; + msg_att_data.folder = folder; + msg_att_data.requestKind = requestKind; + msg_att_data.mLastFetchedSequenceNumber = mLastFetchedSequenceNumber; + msg_att_data.mapping = mapping; + msg_att_data.needsHeader = needsHeader; + msg_att_data.needsBody = needsBody; + msg_att_data.needsFlags = needsFlags; + msg_att_data.needsGmailLabels = needsGmailLabels; + msg_att_data.startUid = startUid; + + mailimap_set_msg_att_handler(mImap, msg_att_handler, &msg_att_data); + + if (fetchByUID) { + r = mailimap_uid_fetch(mImap, imapset, fetch_type, &fetch_result); + } else { + r = mailimap_fetch(mImap, imapset, fetch_type, &fetch_result); + } + + mProgressCallback = NULL; + + mLastFetchedSequenceNumber = msg_att_data.mLastFetchedSequenceNumber; + + mailimap_fetch_type_free(fetch_type); + + mailimap_set_msg_att_handler(mImap, NULL, NULL); + + if (r == MAILIMAP_ERROR_STREAM) { + MCLog("error stream"); + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + MCLog("error parse"); + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + MCLog("error fetch"); + * pError = ErrorFetch; + return NULL; + } + + if ((requestKind & IMAPMessagesRequestKindHeaders) != 0) { + if (result->count() == 0) { + unsigned int count; + + count = clist_count(fetch_result); + if (count > 0) { + requestKind = (IMAPMessagesRequestKind) (requestKind & ~IMAPMessagesRequestKindHeaders); + requestKind = (IMAPMessagesRequestKind) (requestKind | IMAPMessagesRequestKindFullHeaders); + + result = fetchMessages(folder, requestKind, fetchByUID, + imapset, NULL, startUid, progressCallback, pError); + if (result->count() > 0) { + mNeedsMboxMailWorkaround = true; + } + } + } + } + + mailimap_fetch_list_free(fetch_result); + * pError = ErrorNone; + + return result; +} + +Array * IMAPSession::fetchMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + uint32_t firstUID, uint32_t lastUID, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + struct mailimap_set * imapset = mailimap_set_new_interval(firstUID, lastUID); + Array * result = fetchMessages(folder, requestKind, true, imapset, NULL, firstUID, + progressCallback, pError); + mailimap_set_free(imapset); + return result; +} + +Array * IMAPSession::fetchMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + Array * uids, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + struct mailimap_set * imapset = setFromArray(uids); + Array * result = fetchMessages(folder, requestKind, true, imapset, NULL, + (uint32_t) ((Value *) uids->objectAtIndex(0))->unsignedLongValue(), + progressCallback, pError); + mailimap_set_free(imapset); + return result; +} + +Array * IMAPSession::fetchMessagesByNumber(String * folder, IMAPMessagesRequestKind requestKind, + uint32_t firstNumber, uint32_t lastNumber, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + struct mailimap_set * imapset = mailimap_set_new_interval(firstNumber, lastNumber); + Array * result = fetchMessages(folder, requestKind, false, imapset, NULL, 0, + progressCallback, pError); + mailimap_set_free(imapset); + return result; +} + +Array * IMAPSession::fetchMessagesByNumber(String * folder, IMAPMessagesRequestKind requestKind, + Array * numbers, IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + struct mailimap_set * imapset = setFromArray(numbers); + Array * result = fetchMessages(folder, requestKind, false, imapset, NULL, 0, + progressCallback, pError); + mailimap_set_free(imapset); + return result; +} + +static int fetch_rfc822(mailimap * session, + uint32_t msgid, char ** result) +{ + int r; + clist * fetch_list; + struct mailimap_section * section; + struct mailimap_fetch_att * fetch_att; + struct mailimap_fetch_type * fetch_type; + struct mailimap_set * set; + struct mailimap_msg_att * msg_att; + struct mailimap_msg_att_item * item; + int res; + clistiter * cur; + + section = mailimap_section_new(NULL); + fetch_att = mailimap_fetch_att_new_body_peek_section(section); + fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att); + + set = mailimap_set_new_single(msgid); + + r = mailimap_uid_fetch(session, set, fetch_type, &fetch_list); + + mailimap_set_free(set); + mailimap_fetch_type_free(fetch_type); + + if (r != MAILIMAP_NO_ERROR) { + res = r; + goto err; + } + + if (clist_isempty(fetch_list)) { + res = MAILIMAP_ERROR_FETCH; + goto free; + } + + msg_att = (struct mailimap_msg_att *) clist_begin(fetch_list)->data; + + for(cur = clist_begin(msg_att->att_list) ; cur != NULL ; cur = clist_next(cur)) { + item = (struct mailimap_msg_att_item *) clist_content(cur); + + if (item->att_type != MAILIMAP_MSG_ATT_ITEM_STATIC) { + continue; + } + if (item->att_data.att_static->att_type != MAILIMAP_MSG_ATT_BODY_SECTION) { + continue; + } + + * result = item->att_data.att_static->att_data.att_body_section->sec_body_part; + item->att_data.att_static->att_data.att_body_section->sec_body_part = NULL; + mailimap_fetch_list_free(fetch_list); + + return MAILIMAP_NO_ERROR; + } + + res = MAILIMAP_ERROR_FETCH; + +free: + mailimap_fetch_list_free(fetch_list); +err: + return res; +} + +Data * IMAPSession::fetchMessageByUID(String * folder, uint32_t uid, + IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + char * rfc822; + int r; + Data * data; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + mProgressItemsCount = 0; + mProgressCallback = progressCallback; + + rfc822 = NULL; + r = fetch_rfc822(mImap, uid, &rfc822); + if (r == MAILIMAP_NO_ERROR) { + size_t len; + + len = 0; + if (rfc822 != NULL) { + len = strlen(rfc822); + } + bodyProgress((unsigned int) len, (unsigned int) len); + } + mProgressCallback = NULL; + + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorFetch; + return NULL; + } + + if (rfc822 == NULL) { + data = Data::data(); + } + else { + data = Data::dataWithBytes(rfc822, (unsigned int) strlen(rfc822)); + } + + mailimap_nstring_free(rfc822); + * pError = ErrorNone; + + return data; +} + +Data * IMAPSession::fetchMessageAttachmentByUID(String * folder, uint32_t uid, String * partID, + Encoding encoding, unsigned int expectedSize, + IMAPProgressCallback * progressCallback, ErrorCode * pError) +{ + struct mailimap_fetch_type * fetch_type; + struct mailimap_fetch_att * fetch_att; + struct mailimap_section * section; + struct mailimap_section_part * section_part; + clist * sec_list; + Array * partIDArray; + int r; + char * text; + size_t text_length; + Data * data; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + mProgressItemsCount = 0; + mProgressCallback = progressCallback; + bodyProgress(0, expectedSize); + + 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(); + clist_append(sec_list, value); + } + 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); + fetch_type = mailimap_fetch_type_new_fetch_att(fetch_att); + + r = fetch_imap(mImap, uid, fetch_type, &text, &text_length); + mailimap_fetch_type_free(fetch_type); + + bodyProgress(expectedSize, expectedSize); + mProgressCallback = NULL; + + MCLog("had error : %i", r); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorFetch; + return NULL; + } + + data = Data::dataWithBytes(text, (unsigned int) text_length); + data = data->decodedDataUsingEncoding(encoding); + + mailimap_nstring_free(text); + * pError = ErrorNone; + + return data; +} + +Array * IMAPSession::search(String * folder, IMAPSearchKind kind, String * searchString, ErrorCode * pError) +{ + IMAPSearchExpression * expr; + + expr = NULL; + switch (kind) { + case IMAPSearchKindFrom: + expr = IMAPSearchExpression::searchFrom(searchString); + break; + case IMAPSearchKindRecipient: + expr = IMAPSearchExpression::searchRecipient(searchString); + break; + case IMAPSearchKindSubject: + expr = IMAPSearchExpression::searchSubject(searchString); + break; + case IMAPSearchKindContent: + expr = IMAPSearchExpression::searchContent(searchString); + break; + default: + MCAssert(0); + break; + } + return search(folder, expr, pError); +} + +static struct mailimap_search_key * searchKeyFromSearchExpression(IMAPSearchExpression * expression) +{ + switch (expression->kind()) { + case IMAPSearchKindFrom: + { + return mailimap_search_key_new_from(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindRecipient: + { + struct mailimap_search_key * to_search; + struct mailimap_search_key * cc_search; + struct mailimap_search_key * bcc_search; + struct mailimap_search_key * or_search1; + struct mailimap_search_key * or_search2; + + to_search = mailimap_search_key_new_to(strdup(expression->value()->UTF8Characters())); + cc_search = mailimap_search_key_new_cc(strdup(expression->value()->UTF8Characters())); + bcc_search = mailimap_search_key_new_bcc(strdup(expression->value()->UTF8Characters())); + + or_search1 = mailimap_search_key_new_or(to_search, cc_search); + or_search2 = mailimap_search_key_new_or(or_search1, bcc_search); + + return or_search2; + } + case IMAPSearchKindSubject: + { + return mailimap_search_key_new_subject(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindContent: + { + return mailimap_search_key_new_text(strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindHeader: + { + return mailimap_search_key_new_header(strdup(expression->header()->UTF8Characters()), strdup(expression->value()->UTF8Characters())); + } + case IMAPSearchKindOr: + { + return mailimap_search_key_new_or(searchKeyFromSearchExpression(expression->leftExpression()), searchKeyFromSearchExpression(expression->rightExpression())); + } + case IMAPSearchKindAnd: + { + clist * list; + list = clist_new(); + clist_append(list, searchKeyFromSearchExpression(expression->leftExpression())); + clist_append(list, searchKeyFromSearchExpression(expression->rightExpression())); + return mailimap_search_key_new_multiple(list); + } + default: + MCAssert(0); + return NULL; + } +} + +Array * IMAPSession::search(String * folder, IMAPSearchExpression * expression, ErrorCode * pError) +{ + struct mailimap_search_key * key; + + key = searchKeyFromSearchExpression(expression); + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return NULL; + + clist * result_list = NULL; + + int r = mailimap_uid_search(mImap, "utf-8", key, &result_list); + mailimap_search_key_free(key); + MCLog("had error : %i", r); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorFetch; + return NULL; + } + + Array * result = Array::array(); + for(clistiter * cur = clist_begin(result_list) ; cur != NULL ; cur = clist_next(cur)) { + uint32_t * uid = (uint32_t *) clist_content(cur); + result->addObject(Value::valueWithUnsignedLongValue(* uid)); + } + mailimap_search_result_free(result_list); + * pError = ErrorNone; + return result; +} + +void IMAPSession::setupIdle() +{ + // main thread + LOCK(); + mailstream_setup_idle(mImap->imap_stream); + UNLOCK(); +} + +void IMAPSession::idle(String * folder, uint32_t lastKnownUID, ErrorCode * pError) +{ + int r; + + // connection thread + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + if (lastKnownUID != 0) { + Array * msgs; + + msgs = fetchMessagesByUID(folder, IMAPMessagesRequestKindUid, lastKnownUID, 0, + NULL, pError); + if (* pError != ErrorNone) + return; + if (msgs->count() > 0) { + IMAPMessage * msg; + + msg = (IMAPMessage *) msgs->objectAtIndex(0); + if (msg->uid() > lastKnownUID) { + MCLog("found msg UID %u %u", (unsigned int) msg->uid(), (unsigned int) lastKnownUID); + return; + } + } + } + + r = mailimap_idle(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorIdle; + return; + } + + if (!mImap->imap_selection_info->sel_has_exists && !mImap->imap_selection_info->sel_has_recent) { + int r; + r = mailstream_wait_idle(mImap->imap_stream, MAX_IDLE_DELAY); + switch (r) { + case MAILSTREAM_IDLE_ERROR: + case MAILSTREAM_IDLE_CANCELLED: + { + * pError = ErrorConnection; + MCLog("error or cancelled"); + return; + } + case MAILSTREAM_IDLE_INTERRUPTED: + MCLog("interrupted by user"); + break; + case MAILSTREAM_IDLE_HASDATA: + MCLog("something on the socket"); + break; + case MAILSTREAM_IDLE_TIMEOUT: + MCLog("idle timeout"); + break; + } + } + else { + MCLog("found info before idling"); + } + + r = mailimap_idle_done(mImap); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorIdle; + return; + } + * pError = ErrorNone; +} + +void IMAPSession::interruptIdle() +{ + // main thread + LOCK(); + mailstream_interrupt_idle(mImap->imap_stream); + UNLOCK(); +} + +void IMAPSession::unsetupIdle() +{ + // main thread + LOCK(); + mailstream_unsetup_idle(mImap->imap_stream); + UNLOCK(); +} + +void IMAPSession::disconnect() +{ + unsetup(); +} + +HashMap * IMAPSession::identity(String * vendor, String * name, String * version, ErrorCode * pError) +{ + connectIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + struct mailimap_id_params_list * client_identification; + char * dup_name; + char * dup_value; + + client_identification = mailimap_id_params_list_new_empty(); + + if (name != NULL) { + dup_name = strdup("name"); + dup_value = strdup(name->UTF8Characters()); + mailimap_id_params_list_add_name_value(client_identification, dup_name, dup_value); + } + if (version != NULL) { + dup_name = strdup("version"); + dup_value = strdup(version->UTF8Characters()); + mailimap_id_params_list_add_name_value(client_identification, dup_name, dup_value); + } + if (vendor != NULL) { + dup_name = strdup("vendor"); + dup_value = strdup(vendor->UTF8Characters()); + mailimap_id_params_list_add_name_value(client_identification, dup_name, dup_value); + } + + int r; + struct mailimap_id_params_list * server_identification; + r = mailimap_id(mImap, client_identification, &server_identification); + mailimap_id_params_list_free(client_identification); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorIdentity; + return NULL; + } + + HashMap * result = HashMap::hashMap(); + + clistiter * cur; + for(cur = clist_begin(server_identification->idpa_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailimap_id_param * param; + + param = (struct mailimap_id_param *) clist_content(cur); + + String * responseKey; + String * responseValue; + responseKey = String::stringWithUTF8Characters(param->idpa_name); + responseValue = String::stringWithUTF8Characters(param->idpa_value); + result->setObjectForKey(responseKey, responseValue); + } + + mailimap_id_params_list_free(server_identification); + * pError = ErrorNone; + + return result; +} + +void IMAPSession::bodyProgress(unsigned int current, unsigned int maximum) +{ + if (mProgressCallback != NULL) { + mProgressCallback->bodyProgress(this, current, maximum); + } +} + +void IMAPSession::itemsProgress(unsigned int current, unsigned int maximum) +{ + if (mProgressCallback != NULL) { + mProgressCallback->itemsProgress(this, current, maximum); + } +} + +IMAPNamespace * IMAPSession::defaultNamespace() +{ + return mDefaultNamespace; +} + +void IMAPSession::setDefaultNamespace(IMAPNamespace * ns) +{ + MC_SAFE_REPLACE_RETAIN(IMAPNamespace, mDefaultNamespace, ns); +} + +HashMap * IMAPSession::fetchNamespace(ErrorCode * pError) +{ + HashMap * result; + struct mailimap_namespace_data * namespace_data; + int r; + + loginIfNeeded(pError); + if (* pError != ErrorNone) + return NULL; + + result = HashMap::hashMap(); + r = mailimap_namespace(mImap, &namespace_data); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return NULL; + } + else if (hasError(r)) { + * pError = ErrorNamespace; + return NULL; + } + + IMAPNamespace * ns; + + if (namespace_data->ns_personal != NULL) { + ns = new IMAPNamespace(); + ns->importIMAPNamespace(namespace_data->ns_personal); + result->setObjectForKey(IMAPNamespacePersonal, ns); + ns->release(); + } + + if (namespace_data->ns_other != NULL) { + ns = new IMAPNamespace(); + ns->importIMAPNamespace(namespace_data->ns_other); + result->setObjectForKey(IMAPNamespaceOther, ns); + ns->release(); + } + + if (namespace_data->ns_shared != NULL) { + ns = new IMAPNamespace(); + ns->importIMAPNamespace(namespace_data->ns_shared); + result->setObjectForKey(IMAPNamespaceOther, ns); + ns->release(); + } + + mailimap_namespace_data_free(namespace_data); + * pError = ErrorNone; + + return result; +} + +void IMAPSession::storeFlags(String * folder, Array * uids, IMAPStoreFlagsRequestKind kind, MessageFlag flags, ErrorCode * pError) +{ + struct mailimap_set * imap_set; + struct mailimap_store_att_flags * store_att_flags; + struct mailimap_flag_list * flag_list; + int r; + clist * setList; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + imap_set = setFromArray(uids); + if (clist_count(imap_set->set_list) == 0) { + return; + } + + setList = splitSet(imap_set, 10); + + flag_list = mailimap_flag_list_new_empty(); + if ((flags & MessageFlagSeen) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_seen(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagAnswered) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_answered(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagFlagged) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flagged(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagDeleted) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_deleted(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagDraft) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_draft(); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagMDNSent) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$MDNSent")); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagForwarded) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$Forwarded")); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagSubmitPending) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$SubmitPending")); + mailimap_flag_list_add(flag_list, f); + } + if ((flags & MessageFlagSubmitted) != 0) { + struct mailimap_flag * f; + + f = mailimap_flag_new_flag_keyword(strdup("$Submitted")); + mailimap_flag_list_add(flag_list, f); + } + + store_att_flags = NULL; + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + + switch (kind) { + case IMAPStoreFlagsRequestKindRemove: + store_att_flags = mailimap_store_att_flags_new_remove_flags_silent(flag_list); + break; + case IMAPStoreFlagsRequestKindAdd: + store_att_flags = mailimap_store_att_flags_new_add_flags_silent(flag_list); + break; + case IMAPStoreFlagsRequestKindSet: + store_att_flags = mailimap_store_att_flags_new_set_flags_silent(flag_list); + break; + } + r = mailimap_uid_store(mImap, current_set, store_att_flags); + + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + goto release; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorStore; + return; + } + } + + release: + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + mailimap_set_free(current_set); + } + clist_free(setList); + mailimap_store_att_flags_free(store_att_flags); + mailimap_set_free(imap_set); + * pError = ErrorNone; +} + +void IMAPSession::storeLabels(String * folder, Array * uids, IMAPStoreFlagsRequestKind kind, Array * labels, ErrorCode * pError) +{ + struct mailimap_set * imap_set; + struct mailimap_msg_att_xgmlabels * xgmlabels; + int r; + clist * setList; + + selectIfNeeded(folder, pError); + if (* pError != ErrorNone) + return; + + imap_set = setFromArray(uids); + if (clist_count(imap_set->set_list) == 0) { + return; + } + + setList = splitSet(imap_set, 10); + + xgmlabels = mailimap_msg_att_xgmlabels_new_empty(); + for(unsigned int i = 0 ; i < labels->count() ; i ++) { + String * label = (String *) labels->objectAtIndex(i); + mailimap_msg_att_xgmlabels_add(xgmlabels, strdup(label->UTF8Characters())); + } + + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + int fl_sign; + + current_set = (struct mailimap_set *) clist_content(iter); + + switch (kind) { + case IMAPStoreFlagsRequestKindRemove: + fl_sign = -1; + break; + case IMAPStoreFlagsRequestKindAdd: + fl_sign = 1; + break; + case IMAPStoreFlagsRequestKindSet: + fl_sign = 0; + break; + } + r = mailimap_uid_store_xgmlabels(mImap, current_set, fl_sign, 1, xgmlabels); + if (r == MAILIMAP_ERROR_STREAM) { + * pError = ErrorConnection; + goto release; + } + else if (r == MAILIMAP_ERROR_PARSE) { + * pError = ErrorParse; + return; + } + else if (hasError(r)) { + * pError = ErrorStore; + return; + } + } + + release: + for(clistiter * iter = clist_begin(setList) ; iter != NULL ; iter = clist_next(iter)) { + struct mailimap_set * current_set; + + current_set = (struct mailimap_set *) clist_content(iter); + mailimap_set_free(current_set); + } + clist_free(setList); + mailimap_msg_att_xgmlabels_free(xgmlabels); + mailimap_set_free(imap_set); + * pError = ErrorNone; +} diff --git a/src/core/imap/MCIMAPSession.h b/src/core/imap/MCIMAPSession.h new file mode 100644 index 00000000..93058c2b --- /dev/null +++ b/src/core/imap/MCIMAPSession.h @@ -0,0 +1,162 @@ +#ifndef __MAILCORE_MCIMAPSESSION_H + +#define __MAILCORE_MCIMAPSESSION_H + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCMessageConstants.h> +#include <libetpan/libetpan.h> + +namespace mailcore { + + extern String * IMAPNamespacePersonal; + extern String * IMAPNamespaceOther; + extern String * IMAPNamespaceShared; + + class IMAPNamespace; + class IMAPSearchExpression; + class IMAPFolder; + class IMAPProgressCallback; + + class IMAPSession : public Object { + private: + String * mHostname; + unsigned int mPort; + String * mUsername; + String * mPassword; + AuthType mAuthType; + ConnectionType mConnectionType; + bool mCheckCertificateEnabled; + bool mVoIPEnabled; + char mDelimiter; + IMAPNamespace * mDefaultNamespace; + + bool mIdleEnabled; + bool mXListEnabled; + String * mWelcomeString; + bool mNeedsMboxMailWorkaround; + time_t mTimeout; + uint32_t mUIDValidity; + uint32_t mUIDNext; + unsigned int mFolderMsgCount; + unsigned int mLastFetchedSequenceNumber; + String * mCurrentFolder; + pthread_mutex_t mIdleLock; + int mState; + mailimap * mImap; + IMAPProgressCallback * mProgressCallback; + unsigned int mProgressItemsCount; + + void init(); + void bodyProgress(unsigned int current, unsigned int maximum); + void itemsProgress(unsigned int current, unsigned int maximum); + bool checkCertificate(); + static void body_progress(size_t current, size_t maximum, void * context); + static void items_progress(size_t current, size_t maximum, void * context); + void setup(); + void unsetup(); + void connectIfNeeded(ErrorCode * pError); + void loginIfNeeded(ErrorCode * pError); + void selectIfNeeded(String * folder, ErrorCode * pError); + char fetchDelimiterIfNeeded(char defaultDelimiter, ErrorCode * pError); + Array * fetchMessages(String * folder, IMAPMessagesRequestKind requestKind, bool fetchByUID, + struct mailimap_set * imapset, HashMap * mapping, uint32_t startUid, + IMAPProgressCallback * progressCallback, ErrorCode * pError); + + public: + IMAPSession(); + virtual ~IMAPSession(); + + //virtual String * className(); + + virtual void setHostname(String * hostname); + virtual String * hostname(); + + virtual void setPort(unsigned int port); + virtual unsigned int port(); + + virtual void setUsername(String * login); + virtual String * username(); + + virtual void setPassword(String * password); + virtual String * password(); + + virtual void setAuthType(AuthType authType); + virtual AuthType authType(); + + virtual void setConnectionType(ConnectionType connectionType); + virtual ConnectionType connectionType(); + + virtual void setTimeout(time_t timeout); + virtual time_t timeout(); + + virtual void setCheckCertificateEnabled(bool enabled); + virtual bool isCheckCertificateEnabled(); + + virtual void setVoIPEnabled(bool enabled); + virtual bool isVoIPEnabled(); + + virtual void setDelimiter(char delimiter); + virtual char delimiter(); + + virtual void setDefaultNamespace(IMAPNamespace * ns); + virtual IMAPNamespace * defaultNamespace(); + + virtual void select(String * folder, ErrorCode * pError); + + virtual Array * /* IMAPFolder */ fetchSubscribedFolders(ErrorCode * pError); + virtual Array * /* IMAPFolder */ fetchAllFolders(ErrorCode * pError); // will use xlist if available + + virtual void renameFolder(String * folder, String * otherName, ErrorCode * pError); + virtual void deleteFolder(String * folder, ErrorCode * pError); + virtual void createFolder(String * folder, ErrorCode * pError); + + virtual void subscribeFolder(String * folder, ErrorCode * pError); + virtual void unsubscribeFolder(String * folder, ErrorCode * pError); + + virtual void appendMessage(String * folder, Data * messageData, MessageFlag flags, + IMAPProgressCallback * progressCallback, uint32_t * createdUID, ErrorCode * pError); + + virtual void copyMessages(String * folder, Array * uidSet, String * destFolder, + Array ** pDestUIDs, ErrorCode * pError); + + virtual void expunge(String * folder, ErrorCode * pError); + + virtual Array * fetchMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + uint32_t firstUID, uint32_t lastUID, IMAPProgressCallback * progressCallback, ErrorCode * pError); + virtual Array * fetchMessagesByNumber(String * folder, IMAPMessagesRequestKind requestKind, + uint32_t firstNumber, uint32_t lastNumber, IMAPProgressCallback * progressCallback, ErrorCode * pError); + virtual Array * fetchMessagesByUID(String * folder, IMAPMessagesRequestKind requestKind, + Array * numbers, IMAPProgressCallback * progressCallback, ErrorCode * pError); + virtual Array * fetchMessagesByNumber(String * folder, IMAPMessagesRequestKind requestKind, + Array * numbers, IMAPProgressCallback * progressCallback, ErrorCode * pError); + virtual Data * fetchMessageByUID(String * folder, uint32_t uid, + IMAPProgressCallback * progressCallback, ErrorCode * pError); + virtual Data * fetchMessageAttachmentByUID(String * folder, uint32_t uid, String * partID, + Encoding encoding, unsigned int expectedSize, + IMAPProgressCallback * progressCallback, ErrorCode * pError); + virtual HashMap * fetchMessageNumberUIDMapping(String * folder, uint32_t fromUID, uint32_t toUID, + ErrorCode * pError); + + virtual void storeFlags(String * folder, Array * uids, IMAPStoreFlagsRequestKind kind, MessageFlag flags, ErrorCode * pError); + virtual void storeLabels(String * folder, Array * uids, IMAPStoreFlagsRequestKind kind, Array * labels, ErrorCode * pError); + + virtual Array * search(String * folder, IMAPSearchKind kind, String * searchString, ErrorCode * pError); + virtual Array * search(String * folder, IMAPSearchExpression * expression, ErrorCode * pError); + + virtual void setupIdle(); + virtual void idle(String * folder, uint32_t lastKnownUID, ErrorCode * pError); + virtual void interruptIdle(); + virtual void unsetupIdle(); + + virtual void connect(ErrorCode * pError); + virtual void disconnect(); + + virtual HashMap * fetchNamespace(ErrorCode * pError); + + virtual void login(ErrorCode * pError); + + virtual HashMap * identity(String * vendor, String * name, String * version, ErrorCode * pError); + }; +} + +#endif diff --git a/src/core/pop/.DS_Store b/src/core/pop/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/src/core/pop/.DS_Store diff --git a/src/core/pop/MCPOP.h b/src/core/pop/MCPOP.h new file mode 100644 index 00000000..7651f9e6 --- /dev/null +++ b/src/core/pop/MCPOP.h @@ -0,0 +1,9 @@ +#ifndef __MAILCORE_MCPOP_H + +#define __MAILCORE_MCPOP_H + +#include <mailcore/MCPOPMessageInfo.h> +#include <mailcore/MCPOPProgressCallback.h> +#include <mailcore/MCPOPSession.h> + +#endif diff --git a/src/core/pop/MCPOPMessageInfo.cc b/src/core/pop/MCPOPMessageInfo.cc new file mode 100644 index 00000000..eafbda9f --- /dev/null +++ b/src/core/pop/MCPOPMessageInfo.cc @@ -0,0 +1,76 @@ +#include "MCPOPMessageInfo.h" + +using namespace mailcore; + +void POPMessageInfo::init() +{ + mIndex = 0; + mSize = 0; + mUid = NULL; +} + +POPMessageInfo::POPMessageInfo() +{ + init(); +} + +POPMessageInfo::POPMessageInfo(POPMessageInfo * other) +{ + init(); + mIndex = other->mIndex; + mSize = other->mSize; + MC_SAFE_REPLACE_COPY(String, mUid, other->mUid); +} + +POPMessageInfo::~POPMessageInfo() +{ + MC_SAFE_RELEASE(mUid); +} + +#if 0 +String * POPMessageInfo::className() +{ + return MCSTR("POPMessageInfo"); +} +#endif + +String * POPMessageInfo::description() +{ + return String::stringWithUTF8Format("<%s:%p %u %s %u>", + MCUTF8(className()), this, mIndex, MCUTF8(mUid), mSize); +} + +Object * POPMessageInfo::copy() +{ + return new POPMessageInfo(this); +} + +void POPMessageInfo::setIndex(unsigned int index) +{ + mIndex = index; +} + +unsigned int POPMessageInfo::index() +{ + return mIndex; +} + +void POPMessageInfo::setSize(unsigned int size) +{ + mSize = size; +} + +unsigned int POPMessageInfo::size() +{ + return mSize; +} + +void POPMessageInfo::setUid(String * uid) +{ + MC_SAFE_REPLACE_COPY(String, mUid, uid); +} + +String * POPMessageInfo::uid() +{ + return mUid; +} diff --git a/src/core/pop/MCPOPMessageInfo.h b/src/core/pop/MCPOPMessageInfo.h new file mode 100644 index 00000000..1e52b695 --- /dev/null +++ b/src/core/pop/MCPOPMessageInfo.h @@ -0,0 +1,38 @@ +#ifndef __MAILCORE_MCPOPMESSAGEINFO_H_ + +#define __MAILCORE_MCPOPMESSAGEINFO_H_ + +#include <mailcore/MCBaseTypes.h> + +namespace mailcore { + + class POPMessageInfo : public Object { + private: + unsigned int mIndex; + unsigned int mSize; + String * mUid; + + void init(); + + public: + POPMessageInfo(); + POPMessageInfo(POPMessageInfo * other); + virtual ~POPMessageInfo(); + + //virtual String * className(); + virtual String * description(); + virtual Object * copy(); + + virtual void setIndex(unsigned int index); + virtual unsigned int index(); + + virtual void setSize(unsigned int size); + virtual unsigned int size(); + + virtual void setUid(String * uid); + virtual String * uid(); + }; + +} + +#endif diff --git a/src/core/pop/MCPOPProgressCallback.h b/src/core/pop/MCPOPProgressCallback.h new file mode 100644 index 00000000..2fc1b66b --- /dev/null +++ b/src/core/pop/MCPOPProgressCallback.h @@ -0,0 +1,15 @@ +#ifndef __MAILCORE_MCPOPPROGRESSCALLBACK_H_ + +#define __MAILCORE_MCPOPPROGRESSCALLBACK_H_ + +namespace mailcore { + + class POPSession; + + class POPProgressCallback { + public: + virtual void bodyProgress(POPSession * session, unsigned int current, unsigned int maximum) {}; + }; +} + +#endif diff --git a/src/core/pop/MCPOPSession.cc b/src/core/pop/MCPOPSession.cc new file mode 100644 index 00000000..eea5af90 --- /dev/null +++ b/src/core/pop/MCPOPSession.cc @@ -0,0 +1,545 @@ +#include "MCPOPSession.h" + +#include <string.h> + +#include "MCPOPMessageInfo.h" +#include "MCPOPProgressCallback.h" +#include "MCMessageHeader.h" + +using namespace mailcore; + +enum { + STATE_DISCONNECTED, + STATE_CONNECTED, + STATE_LOGGEDIN, + STATE_LISTED, +}; + +void POPSession::init() +{ + mHostname = NULL; + mPort = 0; + mUsername = NULL; + mPassword = NULL; + mAuthType = AuthTypeSASLNone; + mConnectionType = ConnectionTypeClear; + mCheckCertificateEnabled = true; + mTimeout = 30; + + mPop = NULL; + mCapabilities = POPCapabilityNone; + mProgressCallback = NULL; + mState = STATE_DISCONNECTED; +} + +POPSession::POPSession() +{ + init(); +} + +POPSession::~POPSession() +{ + MC_SAFE_RELEASE(mHostname); + MC_SAFE_RELEASE(mUsername); + MC_SAFE_RELEASE(mPassword); +} + +#if 0 +String * POPSession::className() +{ + return MCSTR("POPSession"); +} +#endif + +void POPSession::setHostname(String * hostname) +{ + MC_SAFE_REPLACE_COPY(String, mHostname, hostname); +} + +String * POPSession::hostname() +{ + return mHostname; +} + +void POPSession::setPort(unsigned int port) +{ + mPort = port; +} + +unsigned int POPSession::port() +{ + return mPort; +} + +void POPSession::setUsername(String * username) +{ + MC_SAFE_REPLACE_COPY(String, mUsername, username); +} + +String * POPSession::username() +{ + return mUsername; +} + +void POPSession::setPassword(String * password) +{ + MC_SAFE_REPLACE_COPY(String, mPassword, password); +} + +String * POPSession::password() +{ + return mPassword; +} + +void POPSession::setAuthType(AuthType authType) +{ + mAuthType = authType; +} + +AuthType POPSession::authType() +{ + return mAuthType; +} + +void POPSession::setConnectionType(ConnectionType connectionType) +{ + mConnectionType = connectionType; +} + +ConnectionType POPSession::connectionType() +{ + return mConnectionType; +} + +void POPSession::setTimeout(time_t timeout) +{ + mTimeout = timeout; +} + +time_t POPSession::timeout() +{ + return mTimeout; +} + +void POPSession::setCheckCertificateEnabled(bool enabled) +{ + mCheckCertificateEnabled = enabled; +} + +bool POPSession::isCheckCertificateEnabled() +{ + return mCheckCertificateEnabled; +} + +bool POPSession::checkCertificate() +{ + // XXX + return true; +} + +void POPSession::bodyProgress(unsigned int current, unsigned int maximum) +{ + if (mProgressCallback != NULL) { + mProgressCallback->bodyProgress(this, current, maximum); + } +} + +void POPSession::body_progress(size_t current, size_t maximum, void * context) +{ + POPSession * session; + + session = (POPSession *) context; + session->bodyProgress((unsigned int) current, (unsigned int) maximum); +} + +void POPSession::setup() +{ + mPop = mailpop3_new(0, NULL); +} + +void POPSession::unsetup() +{ + if (mPop != NULL) { + if (mPop->pop3_stream != NULL) { + mailstream_close(mPop->pop3_stream); + mPop->pop3_stream = NULL; + } + mailpop3_free(mPop); + mPop = NULL; + } +} + +void POPSession::connectIfNeeded(ErrorCode * pError) +{ + if (mState == STATE_DISCONNECTED) { + connect(pError); + } + else { + * pError = ErrorNone; + } +} + +void POPSession::connect(ErrorCode * pError) +{ + int r; + + setup(); + + switch (mConnectionType) { + case ConnectionTypeStartTLS: + MCLog("connect %s %u", MCUTF8(hostname()), (unsigned int) port()); + r = mailpop3_socket_connect(mPop, MCUTF8(hostname()), port()); + if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorConnection; + return; + } + + MCLog("start TLS"); + r = mailpop3_socket_starttls(mPop); + if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorStartTLSNotAvailable; + return; + } + MCLog("done"); + if (!checkCertificate()) { + * pError = ErrorCertificate; + return; + } + break; + + case ConnectionTypeTLS: + MCLog("connect %s %u", MCUTF8(hostname()), (unsigned int) port()); + r = mailpop3_ssl_connect(mPop, MCUTF8(hostname()), port()); + if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorConnection; + return; + } + if (!checkCertificate()) { + * pError = ErrorCertificate; + return; + } + break; + + default: + r = mailpop3_socket_connect(mPop, MCUTF8(hostname()), port()); + if (r != MAILIMAP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + break; + } + + mailstream_low * low; + String * identifierString; + char * identifier; + + low = mailstream_get_low(mPop->pop3_stream); + if (mUsername != NULL) { + identifierString = String::stringWithUTF8Format("%s@%s:%u", MCUTF8(mUsername), MCUTF8(mHostname), mPort); + } + else { + identifierString = String::stringWithUTF8Format("%s:%u", MCUTF8(mUsername), mPort); + } + identifier = strdup(identifierString->UTF8Characters()); + mailstream_low_set_identifier(low, identifier); + mState = STATE_CONNECTED; + * pError = ErrorNone; +} + +void POPSession::disconnect() +{ + if (mPop == NULL) + return; + + mailpop3_quit(mPop); + mState = STATE_DISCONNECTED; + unsetup(); +} + +void POPSession::loginIfNeeded(ErrorCode * pError) +{ + connectIfNeeded(pError); + if (* pError != ErrorNone) + return; + + if (mState == STATE_CONNECTED) { + login(pError); + } + else { + * pError = ErrorNone; + } +} + +void POPSession::login(ErrorCode * pError) +{ + int r; + const char * utf8username; + const char * utf8password; + + utf8username = MCUTF8(username()); + utf8password = MCUTF8(password()); + if (utf8username == NULL) { + utf8username = ""; + } + if (utf8password == NULL) { + utf8password = ""; + } + + switch (authType()) { + case 0: + default: + r = mailpop3_user(mPop, utf8username); + if (r == MAILPOP3_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorAuthentication; + return; + } + + r = mailpop3_pass(mPop, utf8password); + break; + + case AuthTypeSASLCRAMMD5: + r = mailpop3_auth(mPop, "CRAM-MD5", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLPlain: + r = mailpop3_auth(mPop, "PLAIN", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLGSSAPI: + // needs to be tested + r = mailpop3_auth(mPop, "GSSAPI", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL /* realm */); + break; + + case AuthTypeSASLDIGESTMD5: + r = mailpop3_auth(mPop, "DIGEST-MD5", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLLogin: + r = mailpop3_auth(mPop, "LOGIN", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLSRP: + r = mailpop3_auth(mPop, "SRP", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL); + break; + + case AuthTypeSASLNTLM: + r = mailpop3_auth(mPop, "NTLM", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL /* realm */); + break; + + case AuthTypeSASLKerberosV4: + r = mailpop3_auth(mPop, "KERBEROS_V4", + MCUTF8(hostname()), + NULL, + NULL, + utf8username, utf8username, + utf8password, NULL /* realm */); + break; + } + if (r == MAILPOP3_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorAuthentication; + return; + } + + mState = STATE_LOGGEDIN; + * pError = ErrorNone; +} + +Array * POPSession::fetchMessages(ErrorCode * pError) +{ + int r; + carray * msg_list; + + loginIfNeeded(pError); + if (* pError != ErrorNone) { + return NULL; + } + + r = mailpop3_list(mPop, &msg_list); + if (r == MAILPOP3_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorFetchMessageList; + return NULL; + } + + Array * result = Array::array(); + for(unsigned int i = 0 ; i < carray_count(msg_list) ; i ++) { + struct mailpop3_msg_info * msg_info; + String * uid; + + msg_info = (struct mailpop3_msg_info *) carray_get(msg_list, i); + if (msg_info->msg_uidl == NULL) + continue; + + uid = String::stringWithUTF8Characters(msg_info->msg_uidl); + + POPMessageInfo * info = new POPMessageInfo(); + info->setUid(uid); + info->setIndex(msg_info->msg_index); + result->addObject(info); + info->release(); + } + + * pError = ErrorNone; + mState = STATE_LISTED; + + return result; +} + +void POPSession::listIfNeeded(ErrorCode * pError) +{ + if (mState == STATE_LISTED) { + * pError = ErrorNone; + return; + } + + fetchMessages(pError); +} + +MessageHeader * POPSession::fetchHeader(unsigned int index, ErrorCode * pError) +{ + int r; + char * content; + size_t content_len; + + listIfNeeded(pError); + if (* pError != ErrorNone) { + return NULL; + } + + r = mailpop3_top(mPop, index, 0, &content, &content_len); + if (r == MAILPOP3_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorFetch; + return NULL; + } + + Data * data; + data = new Data(content, (unsigned int) content_len); + MessageHeader * result = new MessageHeader(); + result->importHeadersData(data); + result->autorelease(); + data->release(); + + mailpop3_top_free(content); + * pError = ErrorNone; + + return result; +} + +MessageHeader * POPSession::fetchHeader(POPMessageInfo * msg, ErrorCode * pError) +{ + return fetchHeader(msg->index(), pError); +} + +Data * POPSession::fetchMessage(unsigned int index, POPProgressCallback * callback, ErrorCode * pError) +{ + int r; + char * content; + size_t content_len; + + listIfNeeded(pError); + if (* pError != ErrorNone) { + return NULL; + } + + mProgressCallback = callback; + + r = mailpop3_retr(mPop, index, &content, &content_len); + mProgressCallback = NULL; + if (r == MAILPOP3_ERROR_STREAM) { + * pError = ErrorConnection; + return NULL; + } + else if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorFetch; + return NULL; + } + + Data * result; + result = Data::dataWithBytes(content, (unsigned int) content_len); + mailpop3_retr_free(content); + * pError = ErrorNone; + + return result; +} + +Data * POPSession::fetchMessage(POPMessageInfo * msg, POPProgressCallback * callback, ErrorCode * pError) +{ + return fetchMessage(msg->index(), callback, pError); +} + +void POPSession::deleteMessage(unsigned int index, ErrorCode * pError) +{ + int r; + + listIfNeeded(pError); + if (* pError != ErrorNone) { + return; + } + + r = mailpop3_dele(mPop, index); + if (r == MAILPOP3_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILPOP3_NO_ERROR) { + * pError = ErrorDeleteMessage; + return; + } + + * pError = ErrorNone; +} + +void POPSession::deleteMessage(POPMessageInfo * msg, ErrorCode * pError) +{ + deleteMessage(msg->index(), pError); +} diff --git a/src/core/pop/MCPOPSession.h b/src/core/pop/MCPOPSession.h new file mode 100644 index 00000000..ef6c132b --- /dev/null +++ b/src/core/pop/MCPOPSession.h @@ -0,0 +1,90 @@ +#ifndef __MAILCORE_MCPOPSESSION_H_ + +#define __MAILCORE_MCPOPSESSION_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCMessageConstants.h> +#include <libetpan/libetpan.h> + +namespace mailcore { + + class POPMessageInfo; + class POPProgressCallback; + class MessageHeader; + + class POPSession : public Object { + private: + String * mHostname; + unsigned int mPort; + String * mUsername; + String * mPassword; + AuthType mAuthType; + ConnectionType mConnectionType; + bool mCheckCertificateEnabled; + time_t mTimeout; + + mailpop3 * mPop; + POPCapability mCapabilities; + POPProgressCallback * mProgressCallback; + int mState; + + void init(); + void bodyProgress(unsigned int current, unsigned int maximum); + bool checkCertificate(); + static void body_progress(size_t current, size_t maximum, void * context); + void setup(); + void unsetup(); + void connectIfNeeded(ErrorCode * pError); + void loginIfNeeded(ErrorCode * pError); + void listIfNeeded(ErrorCode * pError); + + public: + POPSession(); + virtual ~POPSession(); + + //virtual String * className(); + + virtual void setHostname(String * hostname); + virtual String * hostname(); + + virtual void setPort(unsigned int port); + virtual unsigned int port(); + + virtual void setUsername(String * login); + virtual String * username(); + + virtual void setPassword(String * password); + virtual String * password(); + + virtual void setAuthType(AuthType authType); + virtual AuthType authType(); + + virtual void setConnectionType(ConnectionType connectionType); + virtual ConnectionType connectionType(); + + virtual void setTimeout(time_t timeout); + virtual time_t timeout(); + + virtual void setCheckCertificateEnabled(bool enabled); + virtual bool isCheckCertificateEnabled(); + + virtual void connect(ErrorCode * pError); + virtual void disconnect(); + + virtual void login(ErrorCode * pError); + + Array * fetchMessages(ErrorCode * pError); + + MessageHeader * fetchHeader(unsigned int index, ErrorCode * pError); + MessageHeader * fetchHeader(POPMessageInfo * msg, ErrorCode * pError); + + Data * fetchMessage(unsigned int index, POPProgressCallback * callback, ErrorCode * pError); + Data * fetchMessage(POPMessageInfo * msg, POPProgressCallback * callback, ErrorCode * pError); + + void deleteMessage(unsigned int index, ErrorCode * pError); + void deleteMessage(POPMessageInfo * msg, ErrorCode * pError); + }; + +} + +#endif diff --git a/src/core/rfc822/.DS_Store b/src/core/rfc822/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/src/core/rfc822/.DS_Store diff --git a/src/core/rfc822/MCAttachment.cc b/src/core/rfc822/MCAttachment.cc new file mode 100644 index 00000000..856a756f --- /dev/null +++ b/src/core/rfc822/MCAttachment.cc @@ -0,0 +1,444 @@ +#include "MCAttachment.h" + +#include "MCMultipart.h" +#include "MCMessagePart.h" +#include "MCMessageHeader.h" +#include "MCMessageConstants.h" + +#include <stdlib.h> +#include <string.h> + +using namespace mailcore; + +String * Attachment::mimeTypeForFilename(String * filename) +{ + // TODO: read from a file + String * ext; + + ext = filename->pathExtension()->lowercaseString(); + if (ext->isEqual(MCSTR("jpg"))) { + return MCSTR("image/jpeg"); + } + else if (ext->isEqual(MCSTR("jpeg"))) { + return MCSTR("image/jpeg"); + } + else if (ext->isEqual(MCSTR("png"))) { + return MCSTR("image/png"); + } + else if (ext->isEqual(MCSTR("gif"))) { + return MCSTR("image/gif"); + } + else if (ext->isEqual(MCSTR("html"))) { + return MCSTR("text/html"); + } + else if (ext->isEqual(MCSTR("txt"))) { + return MCSTR("text/plain"); + } + return NULL; +} + +Attachment * Attachment::attachmentWithContentOfFile(String * filename) +{ + Attachment * attachment; + String * mimeType; + Data * data; + + attachment = new Attachment(); + data = Data::dataWithContentsOfFile(filename); + mimeType = Attachment::mimeTypeForFilename(filename); + if (mimeType != NULL) { + attachment->setMimeType(mimeType); + } + attachment->setFilename(filename->lastPathComponent()); + attachment->setData(data); + + return (Attachment *) attachment->autorelease(); +} + +Attachment * Attachment::attachmentWithHTMLString(String * htmlString) +{ + Data * data; + Attachment * attachment; + + attachment = new Attachment(); + attachment->setInlineAttachment(true); + attachment->setMimeType(MCSTR("text/html")); + data = htmlString->dataUsingEncoding("utf-8"); + attachment->setData(data); + + return (Attachment *) attachment->autorelease(); +} + +Attachment * Attachment::attachmentWithRFC822Message(Data * messageData) +{ + Attachment * attachment; + + attachment = new Attachment(); + attachment->setMimeType(MCSTR("message/rfc822")); + attachment->setData(messageData); + + return (Attachment *) attachment->autorelease(); +} + +Attachment * Attachment::attachmentWithText(String * text) +{ + Data * data; + Attachment * attachment; + + attachment = new Attachment(); + attachment->setInlineAttachment(true); + attachment->setMimeType(MCSTR("text/plain")); + data = text->dataUsingEncoding("utf-8"); + attachment->setData(data); + + return (Attachment *) attachment->autorelease(); +} + +void Attachment::init() +{ + mData = NULL; + setMimeType(MCSTR("application/octet-stream")); +} + +Attachment::Attachment() +{ + init(); +} + +Attachment::Attachment(Attachment * other) : AbstractPart(other) +{ + init(); + MC_SAFE_REPLACE_RETAIN(Data, mData, other->mData); +} + +Attachment::~Attachment() +{ + MC_SAFE_RELEASE(mData); +} + +String * Attachment::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p\n", className()->UTF8Characters(), this); + if (filename() != NULL) { + result->appendUTF8Format("filename: %s\n", filename()->UTF8Characters()); + } + if (mimeType() != NULL) { + result->appendUTF8Format("mime type: %s\n", mimeType()->UTF8Characters()); + } + if (charset() != NULL) { + result->appendUTF8Format("charset: %s\n", charset()->UTF8Characters()); + } + if (contentID() != NULL) { + result->appendUTF8Format("content-ID: %s\n", contentID()->UTF8Characters()); + } + if (contentLocation() != NULL) { + result->appendUTF8Format("content-location: %s\n", contentLocation()->UTF8Characters()); + } + result->appendUTF8Format("inline: %i\n", isInlineAttachment()); + if (mData != NULL) { + result->appendUTF8Format("data: %i bytes\n", mData->length()); + } + else { + result->appendUTF8Format("no data\n"); + } + result->appendUTF8Format(">"); + + return result; +} + +#if 0 +String * Attachment::className() +{ + return MCSTR("Attachment"); +} +#endif + +Object * Attachment::copy() +{ + return new Attachment(this); +} + +void Attachment::setData(Data * data) +{ + MC_SAFE_REPLACE_RETAIN(Data, mData, data); +} + +Data * Attachment::data() +{ + return mData; +} + +AbstractPart * Attachment::attachmentsWithMIME(struct mailmime * mime) +{ + return attachmentsWithMIMEWithMain(mime, true); +} + +void Attachment::fillMultipartSubAttachments(AbstractMultipart * multipart, struct mailmime * mime) +{ + switch (mime->mm_type) { + case MAILMIME_MULTIPLE: + { + clistiter * cur; + Array * subAttachments = Array::array(); + for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailmime * submime; + AbstractPart * subAttachment; + + submime = (struct mailmime *) clist_content(cur); + subAttachment = attachmentsWithMIMEWithMain(submime, false); + subAttachments->addObject(subAttachment); + } + + multipart->setParts(subAttachments); + break; + } + } +} + +AbstractPart * Attachment::attachmentsWithMIMEWithMain(struct mailmime * mime, bool isMain) +{ + switch (mime->mm_type) { + case MAILMIME_SINGLE: + { + Attachment * attachment; + attachment = attachmentWithSingleMIME(mime); + return attachment; + } + case MAILMIME_MULTIPLE: + { + if ((mime->mm_content_type != NULL) && (mime->mm_content_type->ct_subtype != NULL) && + (strcasecmp(mime->mm_content_type->ct_subtype, "alternative") == 0)) { + Multipart * attachment; + attachment = new Multipart(); + attachment->setPartType(PartTypeMultipartAlternative); + fillMultipartSubAttachments(attachment, mime); + return (Multipart *) attachment->autorelease(); + } + else if ((mime->mm_content_type != NULL) && (mime->mm_content_type->ct_subtype != NULL) && + (strcasecmp(mime->mm_content_type->ct_subtype, "related") == 0)) { + Multipart * attachment; + attachment = new Multipart(); + attachment->setPartType(PartTypeMultipartRelated); + fillMultipartSubAttachments(attachment, mime); + return (Multipart *) attachment->autorelease(); + } + else { + Multipart * attachment; + attachment = new Multipart(); + fillMultipartSubAttachments(attachment, mime); + return (Multipart *) attachment->autorelease(); + } + } + case MAILMIME_MESSAGE: + { + if (isMain) { + AbstractPart * attachment; + attachment = attachmentsWithMIMEWithMain(mime->mm_data.mm_message.mm_msg_mime, false); + return attachment; + } + else { + MessagePart * messagePart; + messagePart = attachmentWithMessageMIME(mime); + return messagePart; + } + } + } + + return NULL; +} + +Encoding Attachment::encodingForMIMEEncoding(struct mailmime_mechanism * mechanism, int defaultMimeEncoding) +{ + Encoding mimeEncoding = (Encoding) defaultMimeEncoding; + + if (mechanism != NULL) { + mimeEncoding = (Encoding) mechanism->enc_type; + } + + switch ((int) mimeEncoding) { + default: + case MAILMIME_MECHANISM_ERROR: + return EncodingOther; + case MAILMIME_MECHANISM_7BIT: + return Encoding7Bit; + case MAILMIME_MECHANISM_8BIT: + return Encoding8Bit; + case MAILMIME_MECHANISM_BINARY: + return EncodingBinary; + case MAILMIME_MECHANISM_QUOTED_PRINTABLE: + return EncodingQuotedPrintable; + case MAILMIME_MECHANISM_BASE64: + return EncodingBase64; + case MAILMIME_MECHANISM_TOKEN: + if (mechanism == NULL) + return Encoding8Bit; + if (mechanism->enc_token == NULL) + return Encoding8Bit; + + if (strcasecmp(mechanism->enc_token, "x-uuencode") == 0) { + return EncodingUUEncode; + } + else { + return EncodingOther; + } + } +} + +static const char * get_discrete_type(struct mailmime_discrete_type * discrete_type) +{ + switch (discrete_type->dt_type) { + case MAILMIME_DISCRETE_TYPE_TEXT: + return "text"; + + case MAILMIME_DISCRETE_TYPE_IMAGE: + return "image"; + + case MAILMIME_DISCRETE_TYPE_AUDIO: + return "audio"; + + case MAILMIME_DISCRETE_TYPE_VIDEO: + return "video"; + + case MAILMIME_DISCRETE_TYPE_APPLICATION: + return "application"; + + case MAILMIME_DISCRETE_TYPE_EXTENSION: + return discrete_type->dt_extension; + } + + return NULL; +} + +static const char * +get_composite_type(struct mailmime_composite_type * composite_type) +{ + switch (composite_type->ct_type) { + case MAILMIME_COMPOSITE_TYPE_MESSAGE: + return "message"; + + case MAILMIME_COMPOSITE_TYPE_MULTIPART: + return "multipart"; + + case MAILMIME_COMPOSITE_TYPE_EXTENSION: + return composite_type->ct_token; + } + + return NULL; +} + +static char * get_content_type_str(struct mailmime_content * content) +{ + const char * str; + char * result; + const char * subtype; + + if (content == NULL) { + return strdup("unknown/unknown"); + } + + str = "unknown"; + + switch (content->ct_type->tp_type) { + case MAILMIME_TYPE_DISCRETE_TYPE: + str = get_discrete_type(content->ct_type->tp_data.tp_discrete_type); + break; + + case MAILMIME_TYPE_COMPOSITE_TYPE: + str = get_composite_type(content->ct_type->tp_data.tp_composite_type); + break; + } + + if (str == NULL) + str = "unknown"; + subtype = content->ct_subtype; + if (subtype == NULL) + subtype = "unknown"; + + result = (char *) malloc(strlen(str) + strlen(subtype) + 2); + strcpy(result, str); + strcat(result, "/"); + strcat(result, subtype); + + return result; +} + +Attachment * Attachment::attachmentWithSingleMIME(struct mailmime * mime) +{ + struct mailmime_data * data; + const char * bytes; + size_t length; + Attachment * result; + struct mailmime_single_fields single_fields; + char * str; + char * name; + char * filename; + char * content_id; + char * loc; + Encoding encoding; + + MCAssert(mime->mm_type == MAILMIME_SINGLE); + + result = new Attachment(); + data = mime->mm_data.mm_single; + bytes = data->dt_data.dt_text.dt_data; + length = data->dt_data.dt_text.dt_length; + + 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); + + name = single_fields.fld_content_name; + filename = single_fields.fld_disposition_filename; + content_id = single_fields.fld_id; + loc = single_fields.fld_location; + + MCLog("filename %s", filename); + if (filename != NULL) { + result->setFilename(String::stringByDecodingMIMEHeaderValue(filename)); + } + else if (name != NULL) { + result->setFilename(String::stringByDecodingMIMEHeaderValue(name)); + } + if (content_id != NULL) { + result->setContentID(String::stringWithUTF8Characters(content_id)); + } + if (single_fields.fld_content_charset != NULL) { + result->setCharset(String::stringByDecodingMIMEHeaderValue(single_fields.fld_content_charset)); + } + if (loc != NULL) { + result->setContentLocation(String::stringWithUTF8Characters(loc)); + } + + if (single_fields.fld_disposition != NULL) { + if (single_fields.fld_disposition->dsp_type != NULL) { + if (single_fields.fld_disposition->dsp_type->dsp_type == MAILMIME_DISPOSITION_TYPE_INLINE) { + result->setInlineAttachment(true); + } + } + } + + return (Attachment *) result->autorelease(); +} + +MessagePart * Attachment::attachmentWithMessageMIME(struct mailmime * mime) +{ + MessagePart * attachment; + AbstractPart * mainPart; + + attachment = new MessagePart(); + attachment->header()->importIMFFields(mime->mm_data.mm_message.mm_fields); + mainPart = attachmentsWithMIMEWithMain(mime->mm_data.mm_message.mm_msg_mime, false); + attachment->setMainPart(mainPart); + + return (MessagePart *) attachment->autorelease(); +} diff --git a/src/core/rfc822/MCAttachment.h b/src/core/rfc822/MCAttachment.h new file mode 100644 index 00000000..0fc4fc98 --- /dev/null +++ b/src/core/rfc822/MCAttachment.h @@ -0,0 +1,47 @@ +#ifndef __MAILCORE_MCATTACHMENT_H_ + +#define __MAILCORE_MCATTACHMENT_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractPart.h> +#include <mailcore/MCAbstractMultipart.h> +#include <mailcore/MCMessageConstants.h> + +namespace mailcore { + + class MessagePart; + + class Attachment : public AbstractPart { + private: + Data * mData; + void init(); + static void fillMultipartSubAttachments(AbstractMultipart * multipart, struct mailmime * mime); + static AbstractPart * attachmentsWithMIMEWithMain(struct mailmime * mime, bool isMain); + static Attachment * attachmentWithSingleMIME(struct mailmime * mime); + static MessagePart * attachmentWithMessageMIME(struct mailmime * mime); + static Encoding encodingForMIMEEncoding(struct mailmime_mechanism * mechanism, int defaultMimeEncoding); + + public: + static String * mimeTypeForFilename(String * filename); + static Attachment * attachmentWithContentOfFile(String * filename); + static Attachment * attachmentWithHTMLString(String * htmlString); + static Attachment * attachmentWithRFC822Message(Data * messageData); + static Attachment * attachmentWithText(String * text); + + Attachment(); + Attachment(Attachment * other); + virtual ~Attachment(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual void setData(Data * data); + virtual Data * data(); + + static AbstractPart * attachmentsWithMIME(struct mailmime * mime); + }; + +} + +#endif diff --git a/src/core/rfc822/MCMessageBuilder.cc b/src/core/rfc822/MCMessageBuilder.cc new file mode 100644 index 00000000..62482564 --- /dev/null +++ b/src/core/rfc822/MCMessageBuilder.cc @@ -0,0 +1,706 @@ +#include "MCMessageBuilder.h" + +#include "MCMessageHeader.h" +#include "MCAttachment.h" + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +using namespace mailcore; + +static char * generate_boundary(const char * boundary_prefix); +struct mailmime * part_multiple_new(const char * type, const char * boundary_prefix); +static struct mailmime * +part_new_empty(struct mailmime_content * content, + struct mailmime_fields * mime_fields, + const char * boundary_prefix, + int force_single); + +static struct mailmime * get_multipart_alternative(const char * boundary_prefix) +{ + struct mailmime * mime; + + mime = part_multiple_new("multipart/alternative", boundary_prefix); + + return mime; +} + +static struct mailmime * get_multipart_related(const char * boundary_prefix) +{ + struct mailmime * mime; + + mime = part_multiple_new("multipart/related", boundary_prefix); + + return mime; +} + +static int add_attachment(struct mailmime * mime, + struct mailmime * mime_sub, + const char * boundary_prefix) +{ + struct mailmime * saved_sub; + struct mailmime * mp; + int res; + int r; + + switch (mime->mm_type) { + case MAILMIME_SINGLE: + res = MAILIMF_ERROR_INVAL; + goto err; + + case MAILMIME_MULTIPLE: + r = mailmime_add_part(mime, mime_sub); + if (r != MAILIMF_NO_ERROR) { + res = MAILIMF_ERROR_MEMORY; + goto err; + } + + return MAILIMF_NO_ERROR; + } + + /* MAILMIME_MESSAGE */ + + if (mime->mm_data.mm_message.mm_msg_mime == NULL) { + /* there is no subpart, we can simply attach it */ + + r = mailmime_add_part(mime, mime_sub); + if (r != MAILIMF_NO_ERROR) { + res = MAILIMF_ERROR_MEMORY; + goto err; + } + + return MAILIMF_NO_ERROR; + } + + if (mime->mm_data.mm_message.mm_msg_mime->mm_type == MAILMIME_MULTIPLE && + strcasecmp(mime->mm_data.mm_message.mm_msg_mime->mm_content_type->ct_subtype, "alternative") != 0) { + /* in case the subpart is multipart, simply attach it to the subpart */ + + return mailmime_add_part(mime->mm_data.mm_message.mm_msg_mime, mime_sub); + } + + /* we save the current subpart, ... */ + + saved_sub = mime->mm_data.mm_message.mm_msg_mime; + + /* create a multipart */ + + mp = part_multiple_new("multipart/mixed", boundary_prefix); + if (mp == NULL) { + res = MAILIMF_ERROR_MEMORY; + goto err; + } + + /* detach the saved subpart from the parent */ + + mailmime_remove_part(saved_sub); + + /* the created multipart is the new child of the parent */ + + r = mailmime_add_part(mime, mp); + if (r != MAILIMF_NO_ERROR) { + res = MAILIMF_ERROR_MEMORY; + goto free_mp; + } + + /* then, attach the saved subpart and ... */ + + r = mailmime_add_part(mp, saved_sub); + if (r != MAILIMF_NO_ERROR) { + res = MAILIMF_ERROR_MEMORY; + goto free_saved_sub; + } + + /* the given part to the parent */ + + r = mailmime_add_part(mp, mime_sub); + if (r != MAILIMF_NO_ERROR) { + res = MAILIMF_ERROR_MEMORY; + goto free_saved_sub; + } + + return MAILIMF_NO_ERROR; + +free_mp: + mailmime_free(mp); +free_saved_sub: + mailmime_free(saved_sub); +err: + return res; +} + +static struct mailmime * get_text_part(const char * mime_type, const char * charset, const char * content_id, + const char * text, size_t length, int encoding_type) +{ + struct mailmime_fields * mime_fields; + struct mailmime * mime; + struct mailmime_content * content; + struct mailmime_parameter * param; + struct mailmime_disposition * disposition; + struct mailmime_mechanism * encoding; + char * dup_content_id; + + encoding = mailmime_mechanism_new(encoding_type, NULL); + disposition = mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE, + NULL, NULL, NULL, NULL, (size_t) -1); + dup_content_id = NULL; + if (content_id != NULL) + dup_content_id = strdup(content_id); + mime_fields = mailmime_fields_new_with_data(encoding, + dup_content_id, NULL, disposition, NULL); + + content = mailmime_content_new_with_str(mime_type); + if (charset == NULL) { + param = mailmime_param_new_with_data((char *) "charset", (char *) "utf-8"); + } + else { + param = mailmime_param_new_with_data((char *) "charset", (char *) charset); + } + clist_append(content->ct_parameters, param); + mime = part_new_empty(content, mime_fields, NULL, 1); + mailmime_set_body_text(mime, (char *) text, length); + + return mime; +} + +static struct mailmime * get_plain_text_part(const char * mime_type, const char * charset, const char * content_id, + const char * text, size_t length) +{ + bool needsQuotedPrintable; + int mechanism; + + needsQuotedPrintable = false; + for(size_t i = 0 ; i < length ; i ++) { + if ((text[i] & (1 << 7)) != 0) { + needsQuotedPrintable = true; + } + } + + mechanism = MAILMIME_MECHANISM_7BIT; + if (needsQuotedPrintable) { + mechanism = MAILMIME_MECHANISM_QUOTED_PRINTABLE; + } + return get_text_part(mime_type, charset, content_id, text, length, mechanism); +} + +static struct mailmime * get_other_text_part(const char * mime_type, const char * charset, const char * content_id, + const char * text, size_t length) +{ + return get_text_part(mime_type, charset, content_id, text, length, MAILMIME_MECHANISM_QUOTED_PRINTABLE); +} + +static struct mailmime * get_file_part(const char * filename, const char * mime_type, int is_inline, + const char * content_id, + const char * text, size_t length) +{ + char * disposition_name; + int encoding_type; + struct mailmime_disposition * disposition; + struct mailmime_mechanism * encoding; + struct mailmime_content * content; + struct mailmime * mime; + struct mailmime_fields * mime_fields; + char * dup_content_id; + + disposition_name = NULL; + if (filename != NULL) { + disposition_name = strdup(filename); + } + if (is_inline) { + disposition = mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_INLINE, + disposition_name, NULL, NULL, NULL, (size_t) -1); + } + else { + disposition = mailmime_disposition_new_with_data(MAILMIME_DISPOSITION_TYPE_ATTACHMENT, + disposition_name, NULL, NULL, NULL, (size_t) -1); + } + content = mailmime_content_new_with_str(mime_type); + + encoding_type = MAILMIME_MECHANISM_BASE64; + encoding = mailmime_mechanism_new(encoding_type, NULL); + dup_content_id = NULL; + if (content_id != NULL) + dup_content_id = strdup(content_id); + mime_fields = mailmime_fields_new_with_data(encoding, + dup_content_id, NULL, disposition, NULL); + mime = part_new_empty(content, mime_fields, NULL, 1); + mailmime_set_body_text(mime, (char *) text, length); + + return mime; +} + +static struct mailmime * mime_from_attachment(Attachment * att) +{ + struct mailmime * mime; + Data * data; + int r; + + data = att->data(); + if (data == NULL) { + data = Data::data(); + } + if (att->mimeType()->lowercaseString()->isEqual(MCSTR("message/rfc822"))) { + size_t indx = 0; + r = mailmime_parse(data->bytes(), data->length(), &indx, &mime); + if (r != MAILIMF_NO_ERROR) + return NULL; + } + else if (att->isInlineAttachment() && att->mimeType()->lowercaseString()->isEqual(MCSTR("text/plain"))) { + mime = get_plain_text_part(MMCUTF8(att->mimeType()), MMCUTF8(att->charset()), + MMCUTF8(att->contentID()), + data->bytes(), data->length()); + } + else if (att->isInlineAttachment() && att->mimeType()->lowercaseString()->hasPrefix(MCSTR("text/"))) { + mime = get_other_text_part(MMCUTF8(att->mimeType()), MMCUTF8(att->charset()), + MMCUTF8(att->contentID()), + data->bytes(), data->length()); + } + else { + mime = get_file_part(att->filename()->encodedMIMEHeaderValue()->bytes(), + MMCUTF8(att->mimeType()), att->isInlineAttachment(), + MMCUTF8(att->contentID()), + data->bytes(), data->length()); + } + return mime; +} + +static struct mailmime * multipart_related_from_attachments(Attachment * htmlAttachment, + Array * attachments, const char * boundary_prefix) +{ + if ((attachments != NULL) && (attachments->count() > 0)) { + struct mailmime * submime; + struct mailmime * mime; + + mime = get_multipart_related(boundary_prefix); + + submime = mime_from_attachment(htmlAttachment); + add_attachment(mime, submime, boundary_prefix); + + for(unsigned int i = 0 ; i < attachments->count() ; i ++) { + Attachment * attachment; + + attachment = (Attachment *) attachments->objectAtIndex(i); + submime = mime_from_attachment(attachment); + add_attachment(mime, submime, boundary_prefix); + } + + return mime; + } + else { + struct mailmime * mime; + + mime = mime_from_attachment(htmlAttachment); + + return mime; + } +} + +static struct mailmime * +part_new_empty(struct mailmime_content * content, + struct mailmime_fields * mime_fields, + const char * boundary_prefix, + int force_single) +{ + struct mailmime * build_info; + clist * list; + int r; + int mime_type; + + list = NULL; + + if (force_single) { + mime_type = MAILMIME_SINGLE; + } + else { + switch (content->ct_type->tp_type) { + case MAILMIME_TYPE_DISCRETE_TYPE: + mime_type = MAILMIME_SINGLE; + break; + + case MAILMIME_TYPE_COMPOSITE_TYPE: + switch (content->ct_type->tp_data.tp_composite_type->ct_type) { + case MAILMIME_COMPOSITE_TYPE_MULTIPART: + mime_type = MAILMIME_MULTIPLE; + break; + + case MAILMIME_COMPOSITE_TYPE_MESSAGE: + if (strcasecmp(content->ct_subtype, "rfc822") == 0) + mime_type = MAILMIME_MESSAGE; + else + mime_type = MAILMIME_SINGLE; + break; + + default: + goto err; + } + break; + + default: + goto err; + } + } + + if (mime_type == MAILMIME_MULTIPLE) { + char * attr_name; + char * attr_value; + struct mailmime_parameter * param; + clist * parameters; + char * boundary; + + list = clist_new(); + if (list == NULL) + goto err; + + attr_name = strdup("boundary"); + if (attr_name == NULL) + goto free_list; + + boundary = generate_boundary(boundary_prefix); + attr_value = boundary; + if (attr_name == NULL) { + free(attr_name); + goto free_list; + } + + param = mailmime_parameter_new(attr_name, attr_value); + if (param == NULL) { + free(attr_value); + free(attr_name); + goto free_list; + } + + if (content->ct_parameters == NULL) { + parameters = clist_new(); + if (parameters == NULL) { + mailmime_parameter_free(param); + goto free_list; + } + } + else + parameters = content->ct_parameters; + + r = clist_append(parameters, param); + if (r != 0) { + clist_free(parameters); + mailmime_parameter_free(param); + goto free_list; + } + + if (content->ct_parameters == NULL) + content->ct_parameters = parameters; + } + + build_info = mailmime_new(mime_type, + NULL, 0, mime_fields, content, + NULL, NULL, NULL, list, + NULL, NULL); + if (build_info == NULL) { + clist_free(list); + return NULL; + } + + return build_info; + +free_list: + clist_free(list); +err: + return NULL; +} + +struct mailmime * part_multiple_new(const char * type, const char * boundary_prefix) +{ + struct mailmime_fields * mime_fields; + struct mailmime_content * content; + struct mailmime * mp; + + mime_fields = mailmime_fields_new_empty(); + if (mime_fields == NULL) + goto err; + + content = mailmime_content_new_with_str(type); + if (content == NULL) + goto free_fields; + + mp = part_new_empty(content, mime_fields, boundary_prefix, 0); + if (mp == NULL) + goto free_content; + + return mp; + +free_content: + mailmime_content_free(content); +free_fields: + mailmime_fields_free(mime_fields); +err: + return NULL; +} + +#define MAX_MESSAGE_ID 512 + +static char * generate_boundary(const char * boundary_prefix) +{ + char id[MAX_MESSAGE_ID]; + time_t now; + char name[MAX_MESSAGE_ID]; + long value; + + now = time(NULL); + value = random(); + + gethostname(name, MAX_MESSAGE_ID); + + if (boundary_prefix == NULL) + boundary_prefix = ""; + + snprintf(id, MAX_MESSAGE_ID, "%s%lx_%lx_%x", boundary_prefix, now, value, getpid()); + + return strdup(id); +} + +void MessageBuilder::init() +{ + mHTMLBody = NULL; + mTextBody = NULL; + mAttachments = NULL; + mRelatedAttachments = NULL; + mBoundaryPrefix = NULL; +} + +MessageBuilder::MessageBuilder() +{ + init(); +} + +MessageBuilder::MessageBuilder(MessageBuilder * other) +{ + init(); + setHTMLBody(other->mHTMLBody); + setHTMLBody(other->mTextBody); + setAttachments(other->mAttachments); + setRelatedAttachments(other->mRelatedAttachments); + MC_SAFE_REPLACE_COPY(String, mBoundaryPrefix, other->mBoundaryPrefix); +} + +MessageBuilder::~MessageBuilder() +{ + MC_SAFE_RELEASE(mHTMLBody); + MC_SAFE_RELEASE(mTextBody); + MC_SAFE_RELEASE(mAttachments); + MC_SAFE_RELEASE(mRelatedAttachments); + MC_SAFE_RELEASE(mBoundaryPrefix); +} + +String * MessageBuilder::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p\n", className()->UTF8Characters(), this); + if (header() != NULL) { + result->appendString(header()->description()); + result->appendUTF8Characters("\n"); + } + if (mHTMLBody != NULL) { + result->appendUTF8Characters("-- html body --\n"); + result->appendString(mHTMLBody); + result->appendUTF8Characters("\n"); + } + if (mTextBody != NULL) { + result->appendUTF8Characters("-- text body --\n"); + result->appendString(mTextBody); + result->appendUTF8Characters("\n"); + } + if (mAttachments != NULL) { + result->appendUTF8Characters("-- attachments --\n"); + result->appendString(mAttachments->description()); + result->appendUTF8Characters("\n"); + } + if (mRelatedAttachments != NULL) { + result->appendUTF8Characters("-- related attachments --\n"); + result->appendString(mRelatedAttachments->description()); + result->appendUTF8Characters("\n"); + } + result->appendUTF8Characters(">"); + + return result; +} + +#if 0 +String * MessageBuilder::className() +{ + return MCSTR("MessageBuilder"); +} +#endif + +Object * MessageBuilder::copy() +{ + return new MessageBuilder(this); +} + +void MessageBuilder::setHTMLBody(String * htmlBody) +{ + MC_SAFE_REPLACE_COPY(String, mHTMLBody, htmlBody); +} + +String * MessageBuilder::htmlBody() +{ + return mHTMLBody; +} + +void MessageBuilder::setTextBody(String * textBody) +{ + MC_SAFE_REPLACE_COPY(String, mTextBody, textBody); +} + +String * MessageBuilder::textBody() +{ + return mTextBody; +} + +void MessageBuilder::setAttachments(Array * attachments) +{ + if (attachments != NULL) { + for(unsigned int i = 0 ; i < attachments->count() ; i ++) { + Attachment * attachment = (Attachment *) attachments->objectAtIndex(i); + attachment->setMessage(this); + } + } + MC_SAFE_REPLACE_COPY(Array, mAttachments, attachments); +} + +Array * MessageBuilder::attachments() +{ + return mAttachments; +} + +void MessageBuilder::addAttachment(Attachment * attachment) +{ + if (mAttachments == NULL) { + mAttachments = new Array(); + } + attachment->setMessage(this); + mAttachments->addObject(attachment); +} + +void MessageBuilder::setRelatedAttachments(Array * attachments) +{ + if (attachments != NULL) { + for(unsigned int i = 0 ; i < attachments->count() ; i ++) { + Attachment * attachment = (Attachment *) attachments->objectAtIndex(i); + attachment->setMessage(this); + } + } + MC_SAFE_REPLACE_COPY(Array, mRelatedAttachments, attachments); +} + +Array * MessageBuilder::relatedAttachments() +{ + return mRelatedAttachments; +} + +void MessageBuilder::addRelatedAttachment(Attachment * attachment) +{ + if (mRelatedAttachments == NULL) { + mRelatedAttachments = new Array(); + } + attachment->setMessage(this); + mRelatedAttachments->addObject(attachment); +} + +void MessageBuilder::setBoundaryPrefix(String * boundaryPrefix) +{ + MC_SAFE_REPLACE_COPY(String, mBoundaryPrefix, boundaryPrefix); +} + +String * MessageBuilder::boundaryPrefix() +{ + return mBoundaryPrefix; +} + +Data * MessageBuilder::dataAndFilterBcc(bool filterBcc) +{ + Data * data; + MMAPString * str; + int col; + + struct mailmime * htmlPart; + struct mailmime * textPart; + struct mailmime * altPart; + struct mailmime * mainPart; + + htmlPart = NULL; + textPart = NULL; + altPart = NULL; + mainPart = NULL; + + if (htmlBody() != NULL) { + Attachment * htmlAttachment; + + htmlAttachment = Attachment::attachmentWithHTMLString(htmlBody()); + htmlPart = multipart_related_from_attachments(htmlAttachment, mRelatedAttachments, + MMCUTF8(mBoundaryPrefix)); + } + + if (textBody() != NULL) { + Attachment * textAttachment; + + textAttachment = Attachment::attachmentWithText(textBody()); + textPart = mime_from_attachment(textAttachment); + } + else if (htmlBody() != NULL) { + Attachment * textAttachment; + + textAttachment = Attachment::attachmentWithText(htmlBody()->flattenHTML()); + textPart = mime_from_attachment(textAttachment); + } + + if ((textPart != NULL) && (htmlPart != NULL)) { + altPart = get_multipart_alternative(MMCUTF8(mBoundaryPrefix)); + mailmime_smart_add_part(altPart, textPart); + mailmime_smart_add_part(altPart, htmlPart); + mainPart = altPart; + } + else if (textPart != NULL) { + mainPart = textPart; + } + else if (htmlPart != NULL) { + mainPart = htmlPart; + } + + struct mailimf_fields * fields; + unsigned int i; + struct mailmime * mime; + + fields = header()->createIMFFieldsAndFilterBcc(filterBcc); + + mime = mailmime_new_message_data(NULL); + mailmime_set_imf_fields(mime, fields); + + if (mainPart != NULL) { + add_attachment(mime, mainPart, MMCUTF8(mBoundaryPrefix)); + } + + if (attachments() != NULL) { + for(i = 0 ; i < attachments()->count() ; i ++) { + Attachment * attachment; + struct mailmime * submime; + + attachment = (Attachment *) attachments()->objectAtIndex(i); + submime = mime_from_attachment(attachment); + add_attachment(mime, submime, MMCUTF8(mBoundaryPrefix)); + } + } + + str = mmap_string_new(""); + col = 0; + mailmime_write_mem(str, &col, mime); + data = Data::dataWithBytes(str->str, (unsigned int) str->len); + mmap_string_free(str); + mailmime_free(mime); + + return data; +} + +Data * MessageBuilder::data() +{ + return dataAndFilterBcc(false); +} diff --git a/src/core/rfc822/MCMessageBuilder.h b/src/core/rfc822/MCMessageBuilder.h new file mode 100644 index 00000000..fd05f3dc --- /dev/null +++ b/src/core/rfc822/MCMessageBuilder.h @@ -0,0 +1,55 @@ +#ifndef __MAILCORE_MCMESSAGEBUILDER_H_ + +#define __MAILCORE_MCMESSAGEBUILDER_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractMessage.h> + +namespace mailcore { + + class Attachment; + + class MessageBuilder : public AbstractMessage { + private: + String * mHTMLBody; + String * mTextBody; + Array * mAttachments; + Array * mRelatedAttachments; + String * mBoundaryPrefix; + void init(); + Data * dataAndFilterBcc(bool filterBcc); + + public: + MessageBuilder(); + MessageBuilder(MessageBuilder * other); + virtual ~MessageBuilder(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual void setHTMLBody(String * htmlBody); + virtual String * htmlBody(); + + virtual void setTextBody(String * textBody); + virtual String * textBody(); + + virtual void setAttachments(Array * /* Attachment */ attachments); + virtual Array * /* Attachment */ attachments(); + virtual void addAttachment(Attachment * attachment); + + // attachments (usually images) that are included in HTML. + virtual void setRelatedAttachments(Array * /* Attachment */ attachments); + virtual Array * /* Attachment */ relatedAttachments(); + virtual void addRelatedAttachment(Attachment * attachment); + + // When boundary needs to be prefixed (to go through spam filters). + virtual void setBoundaryPrefix(String * boundaryPrefix); + virtual String * boundaryPrefix(); + + virtual Data * data(); + }; + +}; + +#endif diff --git a/src/core/rfc822/MCMessageParser.cc b/src/core/rfc822/MCMessageParser.cc new file mode 100644 index 00000000..f301ce3e --- /dev/null +++ b/src/core/rfc822/MCMessageParser.cc @@ -0,0 +1,82 @@ +#include "MCMessageParser.h" + +#include "MCAttachment.h" +#include "MCMessageHeader.h" + +using namespace mailcore; + +MessageParser * MessageParser::messageParserWithData(Data * data) +{ + MessageParser * parser = new MessageParser(data); + return (MessageParser *) parser->autorelease(); +} + +void MessageParser::init() +{ + mData = NULL; + mMainPart = NULL; +} + +MessageParser::MessageParser(Data * data) +{ + init(); + mData = (Data *) data->retain(); + + mailmessage * msg; + struct mailmime * mime; + + msg = data_message_init(data->bytes(), data->length()); + mailmessage_get_bodystructure(msg, &mime); + mMainPart = (AbstractPart *) Attachment::attachmentsWithMIME(msg->msg_mime)->retain(); + MCLog("%s:%p ", MCUTF8(mMainPart->className()), mMainPart); + MCLog("%s:%p ", MCUTF8(mMainPart->description()), mMainPart); + mMainPart->setMessage(this); + header()->importIMFFields(msg->msg_fields); + mailmessage_free(msg); +} + +MessageParser::MessageParser(MessageParser * other) +{ + init(); + MC_SAFE_REPLACE_RETAIN(Data, mData, other->mData); + MC_SAFE_REPLACE_RETAIN(AbstractPart, mMainPart, other->mMainPart); +} + +MessageParser::~MessageParser() +{ + MC_SAFE_RELEASE(mMainPart); + MC_SAFE_RELEASE(mData); +} + +AbstractPart * MessageParser::mainPart() +{ + return mMainPart; +} + +Data * MessageParser::data() +{ + return mData; +} + +String * MessageParser::description() +{ + String * result = String::string(); + result->appendUTF8Format("<%s:%p ", MCUTF8(className()), this); + result->appendUTF8Format("<%p>", mMainPart); + result->appendString(mMainPart->description()); + result->appendUTF8Characters(">"); + + return result; +} + +#if 0 +String * MessageParser::className() +{ + return MCSTR("MessageParser"); +} +#endif + +Object * MessageParser::copy() +{ + return new MessageParser(this); +} diff --git a/src/core/rfc822/MCMessageParser.h b/src/core/rfc822/MCMessageParser.h new file mode 100644 index 00000000..a3ece61c --- /dev/null +++ b/src/core/rfc822/MCMessageParser.h @@ -0,0 +1,34 @@ +#ifndef __MAILCORE_MCPARSEDMESSAGE_H_ + +#define __MAILCORE_MCPARSEDMESSAGE_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractMessage.h> +#include <mailcore/MCAbstractPart.h> + +namespace mailcore { + + class MessageParser : public AbstractMessage { + private: + Data * mData; + AbstractPart * mMainPart; + void init(); + + public: + static MessageParser * messageParserWithData(Data * data); + + MessageParser(Data * data); + MessageParser(MessageParser * other); + virtual ~MessageParser(); + + virtual String * description(); + //virtual String * className(); + virtual Object * copy(); + + virtual AbstractPart * mainPart(); + virtual Data * data(); + }; + +}; + +#endif diff --git a/src/core/rfc822/MCMessagePart.cc b/src/core/rfc822/MCMessagePart.cc new file mode 100644 index 00000000..8a2122c3 --- /dev/null +++ b/src/core/rfc822/MCMessagePart.cc @@ -0,0 +1,27 @@ +#include "MCMessagePart.h" + +using namespace mailcore; + +MessagePart::MessagePart() +{ +} + +MessagePart::MessagePart(MessagePart * other) : AbstractMessagePart(other) +{ +} + +MessagePart::~MessagePart() +{ +} + +#if 0 +String * MessagePart::className() +{ + return MCSTR("MessagePart"); +} +#endif + +Object * MessagePart::copy() +{ + return new MessagePart(this); +} diff --git a/src/core/rfc822/MCMessagePart.h b/src/core/rfc822/MCMessagePart.h new file mode 100644 index 00000000..3f20b3a8 --- /dev/null +++ b/src/core/rfc822/MCMessagePart.h @@ -0,0 +1,22 @@ +#ifndef __MAILCORE_MCMESSAGEPART_H_ + +#define __MAILCORE_MCMESSAGEPART_H_ + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractMessagePart.h> + +namespace mailcore { + + class MessagePart : public AbstractMessagePart { + public: + MessagePart(); + MessagePart(MessagePart * other); + virtual ~MessagePart(); + + //virtual String * className(); + virtual Object * copy(); + }; +} + + +#endif diff --git a/src/core/rfc822/MCMultipart.cc b/src/core/rfc822/MCMultipart.cc new file mode 100644 index 00000000..6ab1f0db --- /dev/null +++ b/src/core/rfc822/MCMultipart.cc @@ -0,0 +1,28 @@ +#include "MCMultipart.h" + +using namespace mailcore; + +Multipart::Multipart() +{ +} + +Multipart::Multipart(Multipart * other) : AbstractMultipart(other) +{ +} + +Multipart::~Multipart() +{ +} + +#if 0 +String * Multipart::className() +{ + return MCSTR("Multipart"); +} +#endif + +Object * Multipart::copy() +{ + return new Multipart(this); +} + diff --git a/src/core/rfc822/MCMultipart.h b/src/core/rfc822/MCMultipart.h new file mode 100644 index 00000000..bdd71c48 --- /dev/null +++ b/src/core/rfc822/MCMultipart.h @@ -0,0 +1,21 @@ +#ifndef __MAILCORE_MCMULTIPART_H + +#define __MAILCORE_MCMULTIPART_H + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCAbstractMultipart.h> + +namespace mailcore { + + class Multipart : public AbstractMultipart { + public: + Multipart(); + Multipart(Multipart * other); + virtual ~Multipart(); + + //virtual String * className(); + virtual Object * copy(); + }; +} + +#endif diff --git a/src/core/rfc822/MCRFC822.h b/src/core/rfc822/MCRFC822.h new file mode 100644 index 00000000..9eb381b8 --- /dev/null +++ b/src/core/rfc822/MCRFC822.h @@ -0,0 +1,11 @@ +#ifndef __MAILCORE_MCRFC822_H + +#define __MAILCORE_MCRFC822_H + +#include <mailcore/MCAttachment.h> +#include <mailcore/MCMessageBuilder.h> +#include <mailcore/MCMessageParser.h> +#include <mailcore/MCMessagePart.h> +#include <mailcore/MCMultipart.h> + +#endif diff --git a/src/core/smtp/.DS_Store b/src/core/smtp/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/src/core/smtp/.DS_Store diff --git a/src/core/smtp/MCSMTP.h b/src/core/smtp/MCSMTP.h new file mode 100644 index 00000000..1f7d17d7 --- /dev/null +++ b/src/core/smtp/MCSMTP.h @@ -0,0 +1,8 @@ +#ifndef __MAILCORE_MCSMTP_H + +#define __MAILCORE_MCSMTP_H + +#include <mailcore/MCSMTPProgressCallback.h> +#include <mailcore/MCSMTPSession.h> + +#endif diff --git a/src/core/smtp/MCSMTPProgressCallback.h b/src/core/smtp/MCSMTPProgressCallback.h new file mode 100644 index 00000000..2537c2c3 --- /dev/null +++ b/src/core/smtp/MCSMTPProgressCallback.h @@ -0,0 +1,15 @@ +#ifndef __MAILCORE_MCSMTPPROGRESSCALLBACK_H_ + +#define __MAILCORE_MCSMTPPROGRESSCALLBACK_H_ + +namespace mailcore { + + class SMTPSession; + + class SMTPProgressCallback { + public: + virtual void bodyProgress(SMTPSession * session, unsigned int current, unsigned int maximum) {}; + }; +} + +#endif diff --git a/src/core/smtp/MCSMTPSession.cc b/src/core/smtp/MCSMTPSession.cc new file mode 100644 index 00000000..52851a08 --- /dev/null +++ b/src/core/smtp/MCSMTPSession.cc @@ -0,0 +1,656 @@ +#include "MCSMTPSession.h" + +#include <string.h> + +#include "MCAddress.h" +#include "MCMessageBuilder.h" +#include "MCMessageParser.h" +#include "MCMessageHeader.h" +#include "MCSMTPProgressCallback.h" + +using namespace mailcore; + +enum { + STATE_DISCONNECTED, + STATE_CONNECTED, + STATE_LOGGEDIN, +}; + +void SMTPSession::init() +{ + mHostname = NULL; + mPort = 0; + mUsername = NULL; + mPassword = NULL; + mAuthType = AuthTypeSASLNone; + mConnectionType = ConnectionTypeClear; + mTimeout = 30; + mCheckCertificateEnabled = true; + mUseHeloIPEnabled = false; + + mSmtp = NULL; + mProgressCallback = NULL; + mState = STATE_DISCONNECTED; + mLastSMTPResponse = NULL; + mLastLibetpanError = 0; + mLastSMTPResponseCode = 0; +} + +SMTPSession::SMTPSession() +{ + init(); +} + +SMTPSession::~SMTPSession() +{ + MCLog("dealloc"); + MC_SAFE_RELEASE(mLastSMTPResponse); + MC_SAFE_RELEASE(mHostname); + MC_SAFE_RELEASE(mUsername); + MC_SAFE_RELEASE(mPassword); + MCLog("dealloc4"); +} + +#if 0 +String * SMTPSession::className() +{ + return MCSTR("SMTPSession"); +} +#endif + +void SMTPSession::setHostname(String * hostname) +{ + MC_SAFE_REPLACE_COPY(String, mHostname, hostname); +} + +String * SMTPSession::hostname() +{ + return mHostname; +} + +void SMTPSession::setPort(unsigned int port) +{ + mPort = port; +} + +unsigned int SMTPSession::port() +{ + return mPort; +} + +void SMTPSession::setUsername(String * username) +{ + MC_SAFE_REPLACE_COPY(String, mUsername, username); +} + +String * SMTPSession::username() +{ + return mUsername; +} + +void SMTPSession::setPassword(String * password) +{ + MC_SAFE_REPLACE_COPY(String, mPassword, password); +} + +String * SMTPSession::password() +{ + return mPassword; +} + +void SMTPSession::setAuthType(AuthType authType) +{ + mAuthType = authType; +} + +AuthType SMTPSession::authType() +{ + return mAuthType; +} + +void SMTPSession::setConnectionType(ConnectionType connectionType) +{ + mConnectionType = connectionType; +} + +ConnectionType SMTPSession::connectionType() +{ + return mConnectionType; +} + +void SMTPSession::setTimeout(time_t timeout) +{ + mTimeout = timeout; +} + +time_t SMTPSession::timeout() +{ + return mTimeout; +} + +void SMTPSession::setCheckCertificateEnabled(bool enabled) +{ + mCheckCertificateEnabled = enabled; +} + +bool SMTPSession::isCheckCertificateEnabled() +{ + return mCheckCertificateEnabled; +} + +bool SMTPSession::checkCertificate() +{ + // XXX + return true; +} + +void SMTPSession::setUseHeloIPEnabled(bool enabled) +{ + mUseHeloIPEnabled = enabled; +} + +bool SMTPSession::useHeloIPEnabled() +{ + return mUseHeloIPEnabled; +} + +void SMTPSession::body_progress(size_t current, size_t maximum, void * context) +{ + SMTPSession * session; + + session = (SMTPSession *) context; + session->bodyProgress((unsigned int) current, (unsigned int) maximum); +} + +void SMTPSession::bodyProgress(unsigned int current, unsigned int maximum) +{ + if (mProgressCallback != NULL) { + mProgressCallback->bodyProgress(this, current, maximum); + } +} + +void SMTPSession::setup() +{ + mSmtp = mailsmtp_new(0, NULL); + mailsmtp_set_timeout(mSmtp, timeout()); + mailsmtp_set_progress_callback(mSmtp, body_progress, this); +} + +void SMTPSession::unsetup() +{ + mailsmtp_free(mSmtp); + mSmtp = NULL; +} + +void SMTPSession::connectIfNeeded(ErrorCode * pError) +{ + if (mState == STATE_DISCONNECTED) { + connect(pError); + } + else { + * pError = ErrorNone; + } +} + +void SMTPSession::connect(ErrorCode * pError) +{ + int r; + + setup(); + + switch (mConnectionType) { + case ConnectionTypeStartTLS: + MCLog("connect %s %u", MCUTF8(hostname()), (unsigned int) port()); + r = mailsmtp_socket_connect(mSmtp, MCUTF8(hostname()), port()); + if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + + MCLog("init"); + if (useHeloIPEnabled()) { + r = mailsmtp_init_with_ip(mSmtp, 1); + } else { + r = mailsmtp_init(mSmtp); + } + if (r == MAILSMTP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + + MCLog("start TLS"); + r = mailsmtp_socket_starttls(mSmtp); + if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorStartTLSNotAvailable; + return; + } + MCLog("done"); + if (!checkCertificate()) { + * pError = ErrorCertificate; + return; + } + + MCLog("init after starttls"); + if (useHeloIPEnabled()) { + r = mailsmtp_init_with_ip(mSmtp, 1); + } else { + r = mailsmtp_init(mSmtp); + } + if (r == MAILSMTP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + + break; + + case ConnectionTypeTLS: + r = mailsmtp_ssl_connect(mSmtp, MCUTF8(mHostname), port()); + if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + if (!checkCertificate()) { + * pError = ErrorCertificate; + return; + } + + MCLog("init"); + if (useHeloIPEnabled()) { + r = mailsmtp_init_with_ip(mSmtp, 1); + } else { + r = mailsmtp_init(mSmtp); + } + if (r == MAILSMTP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + + break; + + default: + r = mailsmtp_socket_connect(mSmtp, MCUTF8(hostname()), port()); + if (r != MAILIMAP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + + MCLog("init"); + if (useHeloIPEnabled()) { + r = mailsmtp_init_with_ip(mSmtp, 1); + } else { + r = mailsmtp_init(mSmtp); + } + if (r == MAILSMTP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorConnection; + return; + } + + break; + } + + mailstream_low * low; + String * identifierString; + char * identifier; + + low = mailstream_get_low(mSmtp->stream); + if (mUsername != NULL) { + identifierString = String::stringWithUTF8Format("%s@%s:%u", MCUTF8(mUsername), MCUTF8(mHostname), port()); + } + else { + identifierString = String::stringWithUTF8Format("%s:%u", MCUTF8(mHostname), port()); + } + identifier = strdup(identifierString->UTF8Characters()); + mailstream_low_set_identifier(low, identifier); + + mState = STATE_CONNECTED; + * pError = ErrorNone; +} + +void SMTPSession::disconnect() +{ + if (mSmtp == NULL) + return; + + mailsmtp_quit(mSmtp); + + unsetup(); + + mState = STATE_DISCONNECTED; +} + +void SMTPSession::loginIfNeeded(ErrorCode * pError) +{ + connectIfNeeded(pError); + if (* pError != ErrorNone) + return; + + if (mState == STATE_CONNECTED) { + login(pError); + } + else { + * pError = ErrorNone; + } +} + +void SMTPSession::login(ErrorCode * pError) +{ + int r; + + if ((username() == NULL) || (password() == NULL)) { + mState = STATE_LOGGEDIN; + * pError = ErrorNone; + return; + } + + if (authType() == 0) { + if (mSmtp->auth & MAILSMTP_AUTH_DIGEST_MD5) { + setAuthType((AuthType) (authType() | AuthTypeSASLDIGESTMD5)); + } + else if (mSmtp->auth & MAILSMTP_AUTH_CRAM_MD5) { + setAuthType((AuthType) (authType() | AuthTypeSASLCRAMMD5)); + } + else if (mSmtp->auth & MAILSMTP_AUTH_GSSAPI) { + setAuthType((AuthType) (authType() | AuthTypeSASLGSSAPI)); + } + else if (mSmtp->auth & MAILSMTP_AUTH_SRP) { + setAuthType((AuthType) (authType() | AuthTypeSASLSRP)); + } + else if (mSmtp->auth & MAILSMTP_AUTH_NTLM) { + setAuthType((AuthType) (authType() | AuthTypeSASLNTLM)); + } + else if (mSmtp->auth & MAILSMTP_AUTH_KERBEROS_V4) { + setAuthType((AuthType) (authType() | AuthTypeSASLKerberosV4)); + } + else if (mSmtp->auth & MAILSMTP_AUTH_PLAIN) { + setAuthType((AuthType) (authType() | AuthTypeSASLPlain)); + } + else if (mSmtp->auth & MAILSMTP_AUTH_LOGIN) { + setAuthType((AuthType) (authType() | AuthTypeSASLLogin)); + } + } + + switch (authType()) { + case 0: + default: + r = mailesmtp_auth_sasl(mSmtp, "PLAIN", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL); + break; + + case AuthTypeSASLCRAMMD5: + r = mailesmtp_auth_sasl(mSmtp, "CRAM-MD5", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL); + break; + + case AuthTypeSASLPlain: + r = mailesmtp_auth_sasl(mSmtp, "PLAIN", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL); + break; + + case AuthTypeSASLGSSAPI: + // needs to be tested + r = mailesmtp_auth_sasl(mSmtp, "GSSAPI", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL); + break; + + case AuthTypeSASLDIGESTMD5: + r = mailesmtp_auth_sasl(mSmtp, "DIGEST-MD5", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL); + break; + + case AuthTypeSASLLogin: + r = mailesmtp_auth_sasl(mSmtp, "LOGIN", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL); + break; + + case AuthTypeSASLSRP: + r = mailesmtp_auth_sasl(mSmtp, "SRP", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL); + break; + + case AuthTypeSASLNTLM: + r = mailesmtp_auth_sasl(mSmtp, "NTLM", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL /* realm */); + break; + + case AuthTypeSASLKerberosV4: + r = mailesmtp_auth_sasl(mSmtp, "KERBEROS_V4", + MCUTF8(mHostname), + NULL, + NULL, + MCUTF8(mUsername), MCUTF8(mUsername), + MCUTF8(mPassword), NULL /* realm */); + break; + } + if (r == MAILSMTP_ERROR_STREAM) { + * pError = ErrorConnection; + return; + } + else if (r != MAILSMTP_NO_ERROR) { + * pError = ErrorAuthentication; + return; + } + + mState = STATE_LOGGEDIN; + * pError = ErrorNone; +} + +void SMTPSession::sendMessage(Address * from, Array * recipients, Data * messageData, + SMTPProgressCallback * callback, ErrorCode * pError) +{ + clist * address_list; + int r; + + messageData = dataWithFilteredBcc(messageData); + + mProgressCallback = callback; + bodyProgress(0, messageData->length()); + + MCLog("setup"); + + MCLog("connect"); + loginIfNeeded(pError); + if (* pError != ErrorNone) { + goto err; + } + + // disable DSN feature for more compatibility + mSmtp->esmtp &= ~MAILSMTP_ESMTP_DSN; + + address_list = esmtp_address_list_new(); + for(unsigned int i = 0 ; i < recipients->count() ; i ++) { + Address * addr = (Address *) recipients->objectAtIndex(i); + esmtp_address_list_add(address_list, (char *) MCUTF8(addr->mailbox()), 0, NULL); + } + MCLog("send"); + if ((mSmtp->esmtp & MAILSMTP_ESMTP_PIPELINING) != 0) { + r = mailesmtp_send_quit(mSmtp, MCUTF8(from->mailbox()), 0, NULL, + address_list, + messageData->bytes(), messageData->length()); + } + else { + r = mailesmtp_send(mSmtp, MCUTF8(from->mailbox()), 0, NULL, + address_list, + messageData->bytes(), messageData->length()); + } + esmtp_address_list_free(address_list); + + String * response; + int responseCode; + + response = NULL; + if (mSmtp->response != NULL) { + response = String::stringWithUTF8Characters(mSmtp->response); + } + responseCode = mSmtp->response_code; + + if ((r == MAILSMTP_ERROR_STREAM) || (r == MAILSMTP_ERROR_CONNECTION_REFUSED)) { + * pError = ErrorConnection; + goto err; + } + else if (r == MAILSMTP_ERROR_EXCEED_STORAGE_ALLOCATION) { + if (response->locationOfString(MCSTR("5.7.0")) != -1) { + * pError = ErrorSendMessageIllegalAttachment; + } + else { + * pError = ErrorStorageLimit; + } + goto err; + } + else if (r == MAILSMTP_ERROR_MAILBOX_UNAVAILABLE) { + * pError = ErrorSendMessageNotAllowed; + goto err; + } + else if (r == MAILSMTP_ERROR_AUTH_REQUIRED) { + * pError = ErrorAuthenticationRequired; + goto err; + } + else if (r != MAILSMTP_NO_ERROR) { + if ((responseCode == 550) && (response->hasPrefix(MCSTR("5.3.4")))) { + * pError = ErrorNeedsConnectToWebmail; + goto err; + } + else { + * pError = ErrorSendMessage; + MC_SAFE_REPLACE_COPY(String, mLastSMTPResponse, response); + mLastLibetpanError = r; + mLastSMTPResponseCode = responseCode; + goto err; + } + } + + bodyProgress(messageData->length(), messageData->length()); + * pError = ErrorNone; + + err: + mProgressCallback = NULL; +} + +Data * SMTPSession::dataWithFilteredBcc(Data * data) +{ + int r; + size_t idx; + struct mailimf_message * msg; + MMAPString * str; + + idx = 0; + r = mailimf_message_parse(data->bytes(), data->length(), &idx, &msg); + if (r != MAILIMF_NO_ERROR) { + return Data::data(); + } + + struct mailimf_fields * fields = msg->msg_fields; + int col = 0; + + str = mmap_string_new(""); + for(clistiter * cur = clist_begin(fields->fld_list) ; cur != NULL ; cur = clist_next(cur)) { + struct mailimf_field * field = (struct mailimf_field *) clist_content(cur); + if (field->fld_type == MAILIMF_FIELD_BCC) { + mailimf_field_free(field); + clist_delete(fields->fld_list, cur); + break; + } + } + mailimf_fields_write_mem(str, &col, fields); + mmap_string_append(str, "\n"); + mmap_string_append_len(str, msg->msg_body->bd_text, msg->msg_body->bd_size); + + Data * result = Data::dataWithBytes(str->str, (unsigned int) str->len); + + mmap_string_free(str); + mailimf_message_free(msg); + + return result; +} + +void SMTPSession::sendMessage(Data * messageData, SMTPProgressCallback * callback, ErrorCode * pError) +{ + AutoreleasePool * pool = new AutoreleasePool(); + MessageParser * parser = new MessageParser(messageData); + Array * recipients = new Array(); + + if (parser->header()->to() != NULL) { + recipients->addObjectsFromArray(parser->header()->to()); + } + if (parser->header()->cc() != NULL) { + recipients->addObjectsFromArray(parser->header()->cc()); + } + if (parser->header()->bcc() != NULL) { + recipients->addObjectsFromArray(parser->header()->bcc()); + } + Address * from = parser->header()->from(); + + sendMessage(from, recipients, messageData, callback, pError); + + recipients->release(); + parser->release(); + pool->release(); +} + +void SMTPSession::sendMessage(MessageBuilder * msg, SMTPProgressCallback * callback, ErrorCode * pError) +{ + Array * recipients = new Array(); + if (msg->header()->to() != NULL) { + recipients->addObjectsFromArray(msg->header()->to()); + } + if (msg->header()->cc() != NULL) { + recipients->addObjectsFromArray(msg->header()->cc()); + } + if (msg->header()->bcc() != NULL) { + recipients->addObjectsFromArray(msg->header()->bcc()); + } + Address * from = msg->header()->from(); + Data * data = msg->data(); + MCAssert(from != NULL); + MCAssert(recipients->count() > 0); + sendMessage(from, recipients, data, callback, pError); + recipients->release(); +} + diff --git a/src/core/smtp/MCSMTPSession.h b/src/core/smtp/MCSMTPSession.h new file mode 100644 index 00000000..45a7c54b --- /dev/null +++ b/src/core/smtp/MCSMTPSession.h @@ -0,0 +1,90 @@ +#ifndef __MAILCORE_MCSMTPSESSION_H + +#define __MAILCORE_MCSMTPSESSION_H + +#include <mailcore/MCBaseTypes.h> +#include <mailcore/MCMessageConstants.h> +#include <libetpan/libetpan.h> + +namespace mailcore { + + class Address; + class SMTPProgressCallback; + class MessageBuilder; + + class SMTPSession : public Object { + private: + String * mHostname; + unsigned int mPort; + String * mUsername; + String * mPassword; + AuthType mAuthType; + ConnectionType mConnectionType; + time_t mTimeout; + bool mCheckCertificateEnabled; + bool mUseHeloIPEnabled; + + mailsmtp * mSmtp; + SMTPProgressCallback * mProgressCallback; + int mState; + String * mLastSMTPResponse; + int mLastLibetpanError; + int mLastSMTPResponseCode; + + void init(); + Data * dataWithFilteredBcc(Data * data); + static void body_progress(size_t current, size_t maximum, void * context); + void bodyProgress(unsigned int current, unsigned int maximum); + void setup(); + void unsetup(); + void connectIfNeeded(ErrorCode * pError); + void loginIfNeeded(ErrorCode * pError); + bool checkCertificate(); + + public: + SMTPSession(); + virtual ~SMTPSession(); + + //virtual String * className(); + + virtual void setHostname(String * hostname); + virtual String * hostname(); + + virtual void setPort(unsigned int port); + virtual unsigned int port(); + + virtual void setUsername(String * username); + virtual String * username(); + + virtual void setPassword(String * password); + virtual String * password(); + + virtual void setAuthType(AuthType authType); + virtual AuthType authType(); + + virtual void setConnectionType(ConnectionType connectionType); + virtual ConnectionType connectionType(); + + virtual void setTimeout(time_t timeout); + virtual time_t timeout(); + + virtual void setCheckCertificateEnabled(bool enabled); + virtual bool isCheckCertificateEnabled(); + + virtual void setUseHeloIPEnabled(bool enabled); + virtual bool useHeloIPEnabled(); + + virtual void connect(ErrorCode * pError); + virtual void disconnect(); + + virtual void login(ErrorCode * pError); + + virtual void sendMessage(Address * from, Array * recipients, Data * messageData, + SMTPProgressCallback * callback, ErrorCode * pError); + virtual void sendMessage(Data * messageData, SMTPProgressCallback * callback, ErrorCode * pError); + virtual void sendMessage(MessageBuilder * msg, SMTPProgressCallback * callback, ErrorCode * pError); + }; + +} + +#endif diff --git a/src/mailcore.h b/src/mailcore.h new file mode 100644 index 00000000..f4198e6d --- /dev/null +++ b/src/mailcore.h @@ -0,0 +1,14 @@ +// +// mailcore.h +// mailcore2 +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#ifndef mailcore2_mailcore_h +#define mailcore2_mailcore_h + +#include <mailcore/MCCore.h> + +#endif diff --git a/tests/main.mm b/tests/main.mm new file mode 100644 index 00000000..db73c89b --- /dev/null +++ b/tests/main.mm @@ -0,0 +1,187 @@ +// +// main.m +// tests +// +// Created by DINH Viêt Hoà on 1/10/13. +// Copyright (c) 2013 MailCore. All rights reserved. +// + +#import <Foundation/Foundation.h> + +#include <unicode/putil.h> +#include <unicode/uclean.h> +#include <unicode/ucnv.h> +#include <mailcore/mailcore.h> + +extern "C" { + extern int mailstream_debug; +} + +static mailcore::String * password = NULL; +static mailcore::String * displayName = NULL; +static mailcore::String * email = NULL; + +class TestOperation : public mailcore::Operation { + void main() + { + MCLog("coin %p", this); + } +}; + +class TestCallback : public mailcore::Object, public mailcore::OperationCallback { + virtual void operationFinished(mailcore::Operation * op) + { + MCLog("callback coin %p %p %s", this, op, MCUTF8DESC(this)); + } +}; + +static mailcore::Data * testMessageBuilder() +{ + mailcore::Address * address = new mailcore::Address(); + address->setDisplayName(displayName); + address->setMailbox(email); + + address->release(); + + mailcore::MessageBuilder * msg = new mailcore::MessageBuilder(); + + msg->header()->setFrom(mailcore::Address::addressWithDisplayName(displayName, email)); + mailcore::Array * to = new mailcore::Array(); + mailcore::Array * bcc = new mailcore::Array(); + to->addObject(mailcore::Address::addressWithDisplayName(MCSTR("Foo Bar"), MCSTR("foobar@to-recipient.org"))); + to->addObject(mailcore::Address::addressWithDisplayName(MCSTR("Other Recipient"), MCSTR("another-foobar@to-recipient.org"))); + bcc->addObject(mailcore::Address::addressWithDisplayName(MCSTR("Hidden Recipient"), MCSTR("foobar@bcc-recipient.org"))); + msg->header()->setTo(to); + msg->header()->setBcc(bcc); + to->release(); + bcc->release(); + msg->header()->setSubject(MCSTR("Mon projet d'été")); + msg->setHTMLBody(MCSTR("<div>Hello <img src=\"cid:1234\"></div>")); + msg->addAttachment(mailcore::Attachment::attachmentWithContentOfFile(MCSTR("first-filename"))); + msg->addAttachment(mailcore::Attachment::attachmentWithContentOfFile(MCSTR("second-filename"))); + mailcore::Attachment * attachment = mailcore::Attachment::attachmentWithContentOfFile(MCSTR("third-image-attachment")); + attachment->setContentID(MCSTR("1234")); + msg->addRelatedAttachment(attachment); + + mailcore::Data * data = msg->data(); + + MCLog("%s", data->bytes()); + + msg->release(); + + return data; +} + +static void testMessageParser(mailcore::Data * data) +{ + mailcore::MessageParser * parser = mailcore::MessageParser::messageParserWithData(data); + MCLog("%s", MCUTF8(parser->description())); +} + +static void testIMAP() +{ + mailcore::IMAPSession * session; + mailcore::ErrorCode error; + + session = new mailcore::IMAPSession(); + session->setHostname(MCSTR("imap.gmail.com")); + session->setPort(993); + session->setUsername(email); + session->setPassword(password); + session->setConnectionType(mailcore::ConnectionTypeTLS); + + mailcore::IMAPMessagesRequestKind requestKind = (mailcore::IMAPMessagesRequestKind) + (mailcore::IMAPMessagesRequestKindHeaders | mailcore::IMAPMessagesRequestKindStructure | + mailcore::IMAPMessagesRequestKindInternalDate | mailcore::IMAPMessagesRequestKindHeaderSubject | + mailcore::IMAPMessagesRequestKindFlags); + mailcore::Array * messages = session->fetchMessagesByUID(MCSTR("INBOX"), + requestKind, 1, 0, NULL, &error); + MCLog("%s", MCUTF8DESC(messages)); + + session->release(); +} + +static void testSMTP(mailcore::Data * data) +{ + mailcore::SMTPSession * smtp; + mailcore::ErrorCode error; + + smtp = new mailcore::SMTPSession(); + + smtp->setHostname(MCSTR("smtp.gmail.com")); + smtp->setPort(25); + smtp->setUsername(email); + smtp->setPassword(password); + smtp->setConnectionType(mailcore::ConnectionTypeStartTLS); + + smtp->sendMessage(data, NULL, &error); + + smtp->release(); +} + +static void testPOP() +{ + mailcore::POPSession * session; + mailcore::ErrorCode error; + + session = new mailcore::POPSession(); + session->setHostname(MCSTR("pop.gmail.com")); + session->setPort(995); + session->setUsername(email); + session->setPassword(password); + session->setConnectionType(mailcore::ConnectionTypeTLS); + + mailcore::Array * messages = session->fetchMessages(&error); + MCLog("%s", MCUTF8DESC(messages)); + + session->release(); +} + +void testAll() +{ + u_setDataDirectory("/usr/local/share/icu"); + + mailcore::AutoreleasePool * pool = new mailcore::AutoreleasePool(); + + mailstream_debug = 1; + + mailcore::Data * data = testMessageBuilder(); + testMessageParser(data); + testSMTP(data); + testIMAP(); + testPOP(); + + mailcore::OperationQueue * queue = new mailcore::OperationQueue(); + + TestCallback * callback = new TestCallback(); + + for(unsigned int i = 0 ; i < 1 ; i ++) { + mailcore::Operation * op = new TestOperation(); + op->setCallback(callback); + queue->addOperation(op); + op->release(); + } + + [[NSRunLoop currentRunLoop] run]; + + queue->release(); + + MCLog("pool release"); + pool->release(); +} + +int main(int argc, const char * argv[]) +{ + + @autoreleasepool { + + email = MCSTR("email@gmail.com"); + password = MCSTR("MyP4ssw0rd"); + displayName = MCSTR("My Email"); + + testAll(); + + } + return 0; +} + |