Files
MailCore/Source/CTSMTPAsyncConnection.m
2013-06-03 15:22:12 -05:00

301 lines
9.3 KiB
Objective-C

/*
* MailCore
*
* Copyright (C) 2010 - Matt Ronge
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the MailCore project nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#import <libetpan/libetpan.h>
#import "CTCoreAddress.h"
#import "CTCoreMessage.h"
#import "CTESMTP.h"
#import "CTSMTP.h"
#import "CTSMTPAsyncConnection.h"
//setup to allow c callback fxn:
CTSMTPAsyncConnection *ptrToSelf;
void
smtpProgress(size_t aCurrent, size_t aTotal) {
if (ptrToSelf != nil) {
float theProgress = (float) aCurrent / (float) aTotal * 100;
[ptrToSelf performSelector:@selector(handleSmtpProgress:)
withObject:[NSNumber numberWithFloat:theProgress]];
}
}
@interface CTSMTPAsyncConnection (PrivateMethods)
- (void)sendMailThread;
- (void)handleSmtpProgress:(NSNumber *)aProgress;
- (void)threadWillExitHandler:(NSNotification *)aNote;
- (void)cleanupAfterThread;
@end
@implementation CTSMTPAsyncConnection
@synthesize message = mMessage;
@synthesize serverSettings = mServerSettings;
@synthesize status = mStatus;
- (id)initWithServer:(NSString *)aServer
username:(NSString *)aUsername
password:(NSString *)aPassword
port:(unsigned int)aPort
connectionType:(CTSMTPConnectionType)connectionType
useAuth:(BOOL)aAuth
delegate:(id <CTSMTPConnectionDelegate>)aDelegate {
self = [super init];
if (self) {
mStatus = CTSMTPAsyncSuccess;
ptrToSelf = self;
mSMTPObj = nil;
mSMTP = NULL;
mMailThread = nil;
mDelegate = aDelegate;
mServerSettings =
[[NSDictionary dictionaryWithObjectsAndKeys:aServer, @"server",
aUsername, @"username",
aPassword, @"password",
[NSNumber numberWithInt:aPort], @"port",
[NSNumber numberWithInt:connectionType], @"connectionType",
[NSNumber numberWithBool:aAuth], @"auth", nil] retain];
//save clients from themselves (they could be leaking us - no dealloc)
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(threadWillExitHandler:)
name:NSThreadWillExitNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self cleanupAfterThread];
[super dealloc];
}
- (void)finalize {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self cleanupAfterThread];
[super finalize];
}
- (void)sendMessageInBackgroundAndNotify:(CTCoreMessage *)aMessage {
if (aMessage == nil) {
NSLog(@"CTCoreMessage param cannot be nil");
return;
}
if (mMailThread != nil && [mMailThread isExecuting]) {
NSLog(@"Can only send one message at a time, we're busy, sheesh.");
return;
}
NSAssert( mMailThread == nil, @"Invalid smtp thread state" );
self.message = aMessage;
//start thread:
mMailThread = [NSThread alloc];
[mMailThread initWithTarget:self selector:@selector(sendMailThread) object:nil];
[mMailThread start];
}
- (void)cancel {
if (![mMailThread isExecuting] || [mMailThread isCancelled]) {
return;
}
//mark thread as cancelled
[mMailThread cancel];
//cancel libetpan smtp stream
mailstream_cancel(mSMTP->stream);
mailstream_close(mSMTP->stream);
mSMTP->stream = NULL;
mailsmtp_free(mSMTP);
mSMTP = NULL;
}
- (BOOL)isBusy {
return (mMailThread != nil && [mMailThread isExecuting]);
}
@end
@implementation CTSMTPAsyncConnection (PrivateMethods)
- (void)sendMailThread {
NSAutoreleasePool *thePool = [[NSAutoreleasePool alloc] init];
BOOL success;
void (*progFxn)(size_t, size_t) = &smtpProgress;
mSMTP = NULL;
mSMTP = mailsmtp_new(30, progFxn);
mSMTPObj = [[CTESMTP alloc] initWithResource:mSMTP];
NSDictionary *theSettings = self.serverSettings;
CTSMTPConnectionType connectionType = [[theSettings objectForKey:@"connectionType"] unsignedIntValue];
NSString* server = [theSettings objectForKey:@"server"];
unsigned int port = [[theSettings objectForKey:@"port"] unsignedIntValue];
BOOL auth = [[theSettings objectForKey:@"auth"] boolValue];
NSString* username = [theSettings objectForKey:@"username"];
NSString* password = [theSettings objectForKey:@"password"];
if (connectionType == CTSMTPConnectionTypeStartTLS || connectionType == CTSMTPConnectionTypePlain) {
success = [mSMTPObj connectToServer:server port:port];
} else if (connectionType == CTSMTPConnectionTypeTLS) {
success = [mSMTPObj connectWithTlsToServer:server port:port];
}
if (!success) {
goto error;
}
if ([mSMTPObj helo] == NO) {
/* The server didn't support ESMTP, so switching to STMP */
[mSMTPObj release];
mSMTPObj = [[CTSMTP alloc] initWithResource:mSMTP];
success = [mSMTPObj helo];
if (!success) {
goto error;
}
}
if (connectionType == CTSMTPConnectionTypeStartTLS) {
success = [mSMTPObj startTLS];
if (!success) {
goto error;
}
}
if (auth) {
success = [mSMTPObj authenticateWithUsername:username password:password server:server];
if (!success) {
goto error;
}
}
CTCoreMessage *theMessage = self.message;
success = [mSMTPObj setFrom:[[[theMessage from] anyObject] email]];
if (!success) {
goto error;
}
NSMutableSet *rcpts = [NSMutableSet set];
[rcpts unionSet:[theMessage to]];
[rcpts unionSet:[theMessage bcc]];
[rcpts unionSet:[theMessage cc]];
success = [mSMTPObj setRecipients:rcpts];
if (!success) {
goto error;
}
NSSet *tmpBcc = theMessage.bcc;
// Temporarily wipe out BCC so it isn't sent with the message
theMessage.bcc = nil;
//send
success = [mSMTPObj setData:[theMessage render]];
theMessage.bcc = tmpBcc;
if (success) {
mStatus = CTSMTPAsyncSuccess;
} else if (success && [mMailThread isCancelled]) {
//libetpan was cancelled from another thread
mStatus = CTSMTPAsyncCanceled;
} else {
mStatus = CTSMTPAsyncError;
}
[thePool drain];
return;
error:
mStatus = CTSMTPAsyncError;
[thePool drain];
}
- (void)handleSmtpProgress:(NSNumber *)aProgress {
//check if cancelled before sending an update
if ([mMailThread isCancelled] && [NSThread currentThread] == mMailThread) {
return;
}
unsigned int theProgress = [aProgress unsignedIntValue];
if (theProgress > mLastProgress) {
mLastProgress = theProgress;
//call delegate
if (mDelegate) {
[mDelegate smtpProgress:mLastProgress];
}
}
}
- (void)threadWillExitHandler:(NSNotification *)aNote {
if ([aNote object] != mMailThread) {
return;
}
if (mDelegate) {
[mDelegate smtpDidFinishSendingMessage:mStatus];
}
[self cleanupAfterThread];
}
- (void)cleanupAfterThread {
[mSMTPObj release];
mSMTPObj = nil;
if (mSMTP) {
if (mSMTP->stream) {
mailstream_cancel(mSMTP->stream);
mailstream_close(mSMTP->stream);
mSMTP->stream = NULL;
}
mailsmtp_free(mSMTP);
mSMTP = NULL;
}
[mServerSettings release];
mServerSettings = nil;
[mMessage release];
mMessage = nil;
[mMailThread release];
mMailThread = nil;
}
@end