You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1056 lines
25 KiB

/*
** CWSMTP.m
**
** Copyright (c) 2001-2007
**
** Author: Ludovic Marcotte <ludovic@Sophos.ca>
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public
** License as published by the Free Software Foundation; either
** version 2.1 of the License, or (at your option) any later version.
**
** This library is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#import "CWSMTP.h"
#import "CWSMTP+Protected.h"
#import "CWConnection.h"
#import "CWConstants.h"
#import "CWInternetAddress.h"
#import "Pantomime/CWMD5.h"
#import <PantomimeFramework/CWMessage.h>
#import "NSData+Extensions.h"
#import <Foundation/NSEnumerator.h>
#import <Foundation/NSNotification.h>
#import "CWConnection.h"
#import "CWThreadSafeArray.h"
#import "CWThreadSafeData.h"
#import "CWOAuthUtils.h"
#import "CWService+Protected.h"
#import <pEpIOSToolboxForExtensions/PEPLogger.h>
// The hostname/domain used to do EHLO/HELO
static NSString *pEpEHLOBase = @"pretty.Easy.privacy";
//
// This function returns the next recipient from the array depending
// if the message is redirected or not.
//
static inline CWInternetAddress *next_recipient(NSMutableArray *theRecipients, BOOL aBOOL)
{
CWInternetAddress *theAddress;
NSUInteger i, count;
count = [theRecipients count];
for (i = 0; i < count; i++)
{
theAddress = [theRecipients objectAtIndex: i];
if (aBOOL)
{
if ([theAddress type] > 3)
{
return theAddress;
}
}
else
{
if ([theAddress type] < 4)
{
return theAddress;
}
}
}
return nil;
}
//
// Private SMTP methods
//
@interface CWSMTP (Private)
- (void) _parseAUTH_CRAM_MD5;
- (void) _parseAUTH_LOGIN;
- (void) _parseAUTH_LOGIN_CHALLENGE;
- (void) _parseAUTH_PLAIN;
- (void) _parseAUTH_OAUTH2;
- (void) _parseAUTHORIZATION;
- (void) _parseDATA;
- (void) _parseEHLO;
- (void) _parseHELO;
- (void) _parseMAIL;
- (void) _parseNOOP;
- (void) _parseQUIT;
- (void) _parseRCPT;
- (void) _parseRSET;
- (void) _parseServerOutput;
@end
//
//
//
@implementation CWSMTP
//
// initializers
//
- (instancetype) initWithName: (NSString *) theName
port: (unsigned int) thePort
transport: (ConnectionTransport)transport
clientCertificate: (SecIdentityRef _Nullable)clientCertificate
{
self = [super initWithName:theName
port:thePort
transport:transport
clientCertificate:clientCertificate];
_sent_recipients = nil;
_recipients = nil;
_message = nil;
_data = nil;
_max_size = 0;
_lastCommand = SMTP_AUTHORIZATION;
// We queue our first "command".
[_queue addObject: AUTORELEASE([[CWSMTPQueueObject alloc] initWithCommand: _lastCommand arguments: @""])];
return self;
}
//
//
//
- (void) dealloc
{
//LogInfo(@"SMTP: -dealloc");
RELEASE(_message);
RELEASE(_data);
RELEASE(_recipients);
RELEASE(_sent_recipients);
//[super dealloc];
}
#pragma mark - Overriden
//
//
//
- (void) cancelRequest
{
dispatch_sync(self.serviceQueue, ^{
[super cancelRequest];
});
}
//
//
//
- (void) close
{
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
if (strongSelf->_connected) {
[strongSelf sendCommand: SMTP_QUIT arguments: @"QUIT"];
}
[super close];
});
}
//
// This method sends a RSET SMTP command.
//
- (void) reset
{
dispatch_sync(self.serviceQueue, ^{
[self sendCommand: SMTP_RSET arguments: @"RSET"];
});
}
//
// This methods reads everything the server sends. Once it judge it has
// read a full response, it calls _parseServerOutput in order to react
// to what we've just received from the server.
//
//
// RFC2821 rationale:
//
// An SMTP reply consists of a three digit number (transmitted as three
// numeric characters) followed by some text unless specified otherwise
// in this document. The number is for use by automata to determine
// what state to enter next; the text is for the human user. The three
// digits contain enough encoded information that the SMTP client need
// not examine the text and may either discard it or pass it on to the
// user, as appropriate. Exceptions are as noted elsewhere in this
// document. In particular, the 220, 221, 251, 421, and 551 reply codes
// are associated with message text that must be parsed and interpreted
// by machines
//
// (...)
//
// The format for multiline replies requires that every line, except the
// last, begin with the reply code, followed immediately by a hyphen,
// "-" (also known as minus), followed by text. The last line will
// begin with the reply code, followed immediately by <SP>, optionally
// some text, and <CRLF>. As noted above, servers SHOULD send the <SP>
// if subsequent text is not sent, but clients MUST be prepared for it
// to be omitted.
//
// For example:
//
// 123-First line
// 123-Second line
// 123-234 text beginning with numbers
// 123 The last line
//
//
// (...)
//
// ... Only the EHLO, EXPN, and HELP
// commands are expected to result in multiline replies in normal
// circumstances, however, multiline replies are allowed for any
// command.
//
- (void) updateRead
{
// Intentionally not serialized on serviceQueue. Must never been called directly by clients.
NSData *aData;
char *buf;
NSUInteger count;
//LogInfo(@"IN UPDATE READ");
[super updateRead];
while ((aData = [_rbuf dropFirstLine]))
{
[_responsesFromServer addObject: aData];
buf = (char *)[aData bytes];
count = [aData length];
// If we got only a response code OR if we're done reading
// a multiline reply, we parse the output!
if (count == 3 || (count > 3 && (*(buf+3) != '-')))
{
[self _parseServerOutput];
}
}
}
//
// This method sends a NOOP SMTP command.
//
- (void) noop
{
dispatch_sync(self.serviceQueue, ^{
[self sendCommand: SMTP_NOOP arguments: @"NOOP"];
});
}
//
//
//
- (int) reconnect
{
dispatch_sync(self.serviceQueue, ^{
[super reconnect];
});
return 0; // In case you wonder see reconnect doc: @result Pending.
}
//
//
//
- (void) startTLS
{
dispatch_sync(self.serviceQueue, ^{
[self sendCommand: SMTP_STARTTLS arguments: @"STARTTLS"];
});
}
//
// This method is used to authenticate ourself to the SMTP server.
//
- (void)authenticate:(NSString *)username
password:(NSString *)password
mechanism:(NSString *)mechanism
{
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
__block typeof(self) strongSelf = weakSelf;
strongSelf->_username = username;
strongSelf->_password = password;
strongSelf->_mechanism = mechanism;
if (!mechanism) {
AUTHENTICATION_FAILED(strongSelf->_delegate, @"");
} else if ([mechanism caseInsensitiveCompare: @"PLAIN"] == NSOrderedSame) {
[self sendCommand: SMTP_AUTH_PLAIN arguments: @"AUTH PLAIN"];
} else if ([mechanism caseInsensitiveCompare: @"LOGIN"] == NSOrderedSame) {
[self sendCommand: SMTP_AUTH_LOGIN arguments: @"AUTH LOGIN"];
} else if ([mechanism caseInsensitiveCompare: @"CRAM-MD5"] == NSOrderedSame) {
[self sendCommand: SMTP_AUTH_CRAM_MD5 arguments: @"AUTH CRAM-MD5"];
} else if ([mechanism caseInsensitiveCompare: @"XOAUTH2"] == NSOrderedSame) {
NSString *clientResponse =
[CWOAuthUtils base64EncodedClientResponseForUser:strongSelf->_username
accessToken:strongSelf->_password];
NSString *args = [NSString stringWithFormat:@"AUTH XOAUTH2 %@", clientResponse];
[self sendCommand: SMTP_AUTH_XOAUTH2 arguments: args];
} else {
// Unknown / Unsupported mechanism
AUTHENTICATION_FAILED(strongSelf->_delegate, mechanism);
}
});
}
#pragma mark - CWTransport
//
// To send a message, we need its data value and the recipients at least.
//
// Depending on what was specified using the "set" methods, we initialize
// what we really want and proceed to send the mail.
//
- (void) sendMessage
{
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
if (!strongSelf->_message && !strongSelf->_data) {
[strongSelf fail];
return;
}
if (!strongSelf->_recipients && strongSelf->_message) {
strongSelf->_recipients =
[NSMutableArray arrayWithArray:[strongSelf->_message recipients]];
if (!strongSelf->_data) {
strongSelf->_data = [strongSelf->_message dataValue];
}
} else if (!strongSelf->_recipients && strongSelf->_data) {
CWMessage *aMessage = [[CWMessage alloc] initWithData: strongSelf->_data];
strongSelf->_message = aMessage;
strongSelf->_recipients = [NSMutableArray arrayWithArray: [aMessage recipients]];
}
strongSelf->_sent_recipients = [strongSelf->_recipients mutableCopy];
NSString *aString;
// We first verify if it's a redirected message
if ([strongSelf->_message resentFrom]) {
strongSelf->_redirected = YES;
aString = [[strongSelf->_message resentFrom] address];
} else {
strongSelf->_redirected = NO;
aString = [[strongSelf->_message from] address];
}
if (strongSelf->_max_size) {
[self sendCommand: SMTP_MAIL
arguments: @"MAIL FROM:<%@> SIZE=%d", aString, [strongSelf->_data length]];
} else {
[self sendCommand: SMTP_MAIL arguments: @"MAIL FROM:<%@>", aString];
}
});
}
//
//
//
- (void) setMessage: (CWMessage *) theMessage
{
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
strongSelf->_data = nil;
strongSelf->_message = theMessage;
});
}
- (CWMessage *) message
{
__block CWMessage *returnee = nil;
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
returnee = strongSelf->_message;
});
return returnee;
}
//
//
//
- (void) setMessageData: (NSData *) theData
{
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
strongSelf->_message = nil;
strongSelf->_data = theData;
});
}
- (NSData *) messageData
{
__block NSData *returnee = nil;
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
returnee = strongSelf->_data;
});
return returnee;
}
//
//
//
- (void) setRecipients: (NSArray *) theRecipients
{
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
strongSelf->_recipients = nil;
if (theRecipients) {
strongSelf->_recipients = [NSMutableArray arrayWithArray: theRecipients];
}
});
}
//
//
//
- (NSArray *) recipients
{
__block NSArray *returnee = nil;
__weak typeof(self) weakSelf = self;
dispatch_sync(self.serviceQueue, ^{
typeof(self) strongSelf = weakSelf;
returnee = strongSelf->_recipients;
});
return returnee;
}
@end
//
// Private methods
//
@implementation CWSMTP (Private)
- (void) _parseAUTH_CRAM_MD5
{
NSData *aData;
aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "334"])
{
NSString *aString;
CWMD5 *aMD5;
// We trim the "334 ", decode the data using base64 and we keep the challenge phrase
aData = [[aData subdataFromIndex: 4] decodeBase64];
aMD5 = [[CWMD5 alloc] initWithData: aData];
[aMD5 computeDigest];
aString = [NSString stringWithFormat: @"%@ %@", _username, [aMD5 hmacAsStringUsingPassword: _password]];
[self bulkWriteData:@[[[aString dataUsingEncoding: _defaultStringEncoding] encodeBase64WithLineLength: 0],
_crlf]];
RELEASE(aMD5);
}
else if ([aData hasCPrefix: "235"])
{
AUTHENTICATION_COMPLETED(_delegate, @"CRAM-MD5");
}
else
{
AUTHENTICATION_FAILED(_delegate, @"CRAM-MD5");
}
}
//
//
//
- (void) _parseAUTH_LOGIN
{
NSData *aData;
aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "334"])
{
NSString *aString;
aString = [[NSString alloc] initWithData: [[_username dataUsingEncoding: _defaultStringEncoding] encodeBase64WithLineLength: 0]
encoding: _defaultStringEncoding];
[self sendCommand: SMTP_AUTH_LOGIN_CHALLENGE arguments: aString];
RELEASE(aString);
}
else
{
AUTHENTICATION_FAILED(_delegate, @"LOGIN");
}
}
//
//! rename if needed.
//
- (void) _parseAUTH_LOGIN_CHALLENGE
{
NSData *aData;
aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "334"])
{
NSString *aString;
aString = [[NSString alloc] initWithData: [[_password dataUsingEncoding: _defaultStringEncoding] encodeBase64WithLineLength: 0]
encoding: _defaultStringEncoding];
[self sendCommand: SMTP_AUTH_LOGIN_CHALLENGE arguments: aString];
RELEASE(aString);
}
else if ([aData hasCPrefix: "235"])
{
AUTHENTICATION_COMPLETED(_delegate, @"LOGIN");
}
else
{
LogInfo(@"Authentification response: |%@|",
[aData asciiString]);
AUTHENTICATION_FAILED(_delegate, @"LOGIN");
}
}
//
//
//
- (void) _parseAUTH_PLAIN
{
NSData *aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "334"]) {
NSString *stringToSend = [NSString stringWithFormat:@"\0%@\0%@", _username, _password];
NSData *dataToSend = [stringToSend dataUsingEncoding:_defaultStringEncoding];
[self bulkWriteData:@[[dataToSend encodeBase64WithLineLength: 0],
_crlf]];
} else if ([aData hasCPrefix: "235"]) {
AUTHENTICATION_COMPLETED(_delegate, @"PLAIN");
}
else {
AUTHENTICATION_FAILED(_delegate, @"PLAIN");
}
}
//
//
//
- (void) _parseAUTH_OAUTH2
{
/*
Example gmail (linebreak not included):
C: AUTH XOAUTH2 dXNlcj1zb21ldXNlckBleGFtcGxlLmNvbQFhdXRoPUJlYXJl
ciB2RjlkZnQ0cW1UYzJOdmIzUmxja0JoZEhSaGRtbHpkR0V1WTI5dENnPT0BAQ==
S: 235 2.7.0 Accepted
*/
NSData *aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "235"])
{
AUTHENTICATION_COMPLETED(_delegate, @"XOAUTH2");
}
else
{
AUTHENTICATION_FAILED(_delegate, @"XOAUTH2");
}
}
//
//
//
- (void) _parseAUTHORIZATION
{
NSData *aData;
aData = [_responsesFromServer lastObject];
// 220 <domain> Service ready
if ([aData hasCPrefix: "220"])
{
[self sendCommand: SMTP_EHLO arguments: [NSString stringWithFormat:@"EHLO %@", pEpEHLOBase]];
}
else
{
// Handle the fact when a server is loaded and can't handle our requests
// right away.
//! unhandled
}
}
//
//
//
- (void) _parseDATA
{
NSData *aData;
aData = [_responsesFromServer lastObject];
// If we can proceed to write the message's data, let's do so.
if ([aData hasCPrefix: "354"])
{
NSMutableData *aMutableData;
NSRange r1, r2;
// We first replace all occurences of LF by CRLF in the Message's data.
//
aMutableData = [[NSMutableData dataWithData: _data] replaceLFWithCRLF];
_data = nil; // save memory by deleting as soon as possible
//
// According to RFC 2821 section 4.5.2, we must check for the character
// sequence "<CRLF>.<CRLF>"; any occurrence have its period duplicated
// to avoid data transparency.
//
r1 = [aMutableData rangeOfCString: "\r\n."];
while (r1.location != NSNotFound)
{
[aMutableData replaceBytesInRange: r1 withBytes: "\r\n.." length: 4];
r1 = [aMutableData rangeOfCString: "\r\n."
options: 0
range: NSMakeRange(NSMaxRange(r1)+1, [aMutableData length]-NSMaxRange(r1)-1)];
}
//
// We now look for the Bcc: header. If it is present, we remove it.
// Some servers, like qmail, do not remove it automatically.
//
r1 = [aMutableData rangeOfCString: "\r\n\r\n"];
r1 = [aMutableData rangeOfCString: "\r\nBcc: "
options: 0
range: NSMakeRange(0,r1.location-1)];
if (r1.location != NSNotFound)
{
// We search for the first \r\n AFTER the Bcc: header and
// replace the whole thing with \r\n.
r2 = [aMutableData rangeOfCString: "\r\n"
options: 0
range: NSMakeRange(NSMaxRange(r1)+1,[aMutableData length]-NSMaxRange(r1)-1)];
[aMutableData replaceBytesInRange: NSMakeRange(r1.location, NSMaxRange(r2)-r1.location)
withBytes: "\r\n"
length: 2];
}
[self bulkWriteData:@[aMutableData,
[NSData dataWithBytes: "\r\n.\r\n" length: 5]]];
}
else if ([aData hasCPrefix: "250"])
{
// The data we wrote in the previous call was sucessfully written.
// We inform the delegate that the mail was sucessfully sent.
PERFORM_SELECTOR_2(_delegate, @selector(messageSent:), PantomimeMessageSent, _message, @"Message");
// Delete memory consuming objects as soon as possible
_data = nil;
_message = nil;
}
else
{
[self fail];
}
}
//
//
//
- (void) _parseEHLO
{
NSData *aData;
NSUInteger i, count;
count = [_responsesFromServer count];
for (i = 0; i < count; i++)
{
aData = [_responsesFromServer objectAtIndex: i];
if ([aData hasCPrefix: "250"])
{
// We parse the SMTP service extensions. For now, we support the SIZE
// and the AUTH extensions. We ignore the rest.
aData = [aData subdataFromIndex: 4];
// We add it to our capabilities
[_capabilities addObject: AUTORELEASE([[NSString alloc] initWithData: aData encoding: _defaultStringEncoding])];
// Example of responses:
//
// AUTH LOGIN
// AUTH=PLAIN CRAM-MD5 DIGEST-MD5
//
if ([aData hasCPrefix: "AUTH"])
{
NSEnumerator *theEnumerator;
id aString;
// We chomp the "AUTH " or "AUTH=" part and we decode our
// supported mechanisms.
theEnumerator = [[[aData subdataFromIndex: 5] componentsSeparatedByCString: " "] objectEnumerator];
while ((aString = [theEnumerator nextObject]))
{
aString = [aString asciiString];
if (![_supportedMechanisms containsObject: aString])
{
[_supportedMechanisms addObject: aString];
}
}
}
//
// SIZE size-param
// size-param ::= [1*DIGIT]
//
// See RFC1870 for detailed information.
//
else if ([aData hasCPrefix: "SIZE"])
{
NSRange aRange;
// We must be careful here. Some broken servers will send only
// 250-SIZE
// and we don't want to parse an inexistant value.
aRange = [aData rangeOfCString: " "];
if (aRange.length)
{
_max_size = atoi([[aData subdataFromIndex: aRange.location+1] cString]);
}
}
}
else
{
// The server doesn't handle EHLO. We send it
// a HELO greeting instead.
[self sendCommand: SMTP_HELO arguments: [NSString stringWithFormat:@"HELO %@", pEpEHLOBase]];
break;
}
}
//! - Inform the delegate if it is ready or not, especially if EHLO failed
PERFORM_SELECTOR_1(_delegate, @selector(serviceInitialized:), PantomimeServiceInitialized);
}
//
//
//
- (void) _parseHELO
{
//! - Implement. + inform the delegate if it's ready or not.
}
//
// This method parses the result received from the server
// after issuing a "MAIL FROM: <>" command.
//
// If the result is successful, we proceed by sending the first RCPT.
//
- (void) _parseMAIL
{
NSData *aData;
aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "250"])
{
// We write the first recipient while respecting the fact
// that we are bouncing or not the message.
PERFORM_SELECTOR_1(_delegate, @selector(transactionInitiationCompleted:), PantomimeTransactionInitiationCompleted);
[self sendCommand: SMTP_RCPT arguments: @"RCPT TO:<%@>", [next_recipient(_sent_recipients, _redirected) address]];
}
else
{
if (!PERFORM_SELECTOR_1(_delegate, @selector(transactionInitiationFailed:), PantomimeTransactionInitiationFailed))
{
[self fail];
}
}
}
//
//
//
- (void) _parseNOOP
{
// Do what?
}
//
//
//
- (void) _parseQUIT
{
NSData *aData;
aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "221"])
{
// Do anything special here?
}
[super close];
}
//
// This method is invoked everytime we sent a recipient to the
// server using the RCPT command.
//
// If it was successful, this command sends the next one, if any
// by first removing the previously sent one from _recipients.
//
- (void) _parseRCPT
{
NSData *aData;
aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "250"])
{
CWInternetAddress *theAddress;
theAddress = next_recipient(_sent_recipients, _redirected);
if (theAddress)
{
[_sent_recipients removeObject: theAddress];
theAddress = next_recipient(_sent_recipients, _redirected);
if (theAddress)
{
[self sendCommand: SMTP_RCPT arguments: @"RCPT TO:<%@>", [theAddress address]];
return;
}
}
// We are done writing the recipients, we now write the content
// of the message.
PERFORM_SELECTOR_2(_delegate, @selector(recipientIdentificationCompleted:), PantomimeRecipientIdentificationCompleted, _recipients, @"Recipients");
[self sendCommand: SMTP_DATA arguments: @"DATA"];
}
else
{
if (!PERFORM_SELECTOR_1(_delegate, @selector(recipientIdentificationFailed:), PantomimeRecipientIdentificationFailed))
{
[self fail];
}
}
}
//
//
//
- (void) _parseRSET
{
NSData *aData;
aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "250"])
{
PERFORM_SELECTOR_1(_delegate, @selector(transactionResetCompleted:), PantomimeTransactionResetCompleted);
}
else
{
PERFORM_SELECTOR_1(_delegate, @selector(transactionResetFailed:), PantomimeTransactionResetFailed);
}
}
//
//
//
- (void) _parseSTARTTLS
{
NSData *aData = [_responsesFromServer lastObject];
if ([aData hasCPrefix: "220"]) {
// We first activate SSL.
[(id<CWConnection>) _connection startTLS];
// We now forget about the initial negotiated state; see RFC2487 for more details,
[_supportedMechanisms removeAllObjects];
[self sendCommand: SMTP_EHLO
arguments: [NSString stringWithFormat:@"EHLO %@", pEpEHLOBase]];
} else {
// The server probably doesn't support TLS. We inform the delegate that the transaction
// initiation failed or that the message wasn't sent.
if (!PERFORM_SELECTOR_1(_delegate, @selector(transactionInitiationFailed:),
PantomimeTransactionInitiationFailed)) {
[self fail];
}
}
}
//
//
//
- (void) _parseServerOutput
{
NSData *aData;
if (![_responsesFromServer count])
{
return;
}
// We read only the first response. The _parseXYZ methods
// will handle multiline responses.
aData = [_responsesFromServer objectAtIndex: 0];
if ([aData hasCPrefix: "421"])
{
//! - lost connection
//LogInfo(@"LOST CONNECTION TO THE SERVER");
[super close];
}
else
{
switch (_lastCommand)
{
case SMTP_AUTH_CRAM_MD5:
[self _parseAUTH_CRAM_MD5];
break;
case SMTP_AUTH_LOGIN:
[self _parseAUTH_LOGIN];
break;
case SMTP_AUTH_LOGIN_CHALLENGE:
[self _parseAUTH_LOGIN_CHALLENGE];
break;
case SMTP_AUTH_PLAIN:
[self _parseAUTH_PLAIN];
break;
case SMTP_AUTH_XOAUTH2:
[self _parseAUTH_OAUTH2];
break;
case SMTP_DATA:
[self _parseDATA];
break;
case SMTP_EHLO:
[self _parseEHLO];
break;
case SMTP_HELO:
[self _parseHELO];
break;
case SMTP_MAIL:
[self _parseMAIL];
break;
case SMTP_NOOP:
[self _parseNOOP];
break;
case SMTP_QUIT:
[self _parseQUIT];
break;
case SMTP_RCPT:
[self _parseRCPT];
break;
case SMTP_RSET:
[self _parseRSET];
break;
case SMTP_STARTTLS:
[self _parseSTARTTLS];
break;
case SMTP_AUTHORIZATION:
[self _parseAUTHORIZATION];
break;
default:
break;
//!
}
}
// We are done parsing this entry...
[_responsesFromServer removeAllObjects];
// We remove the last object of the queue....
if ([_queue lastObject])
{
[_queue removeLastObject];
}
[self sendCommand: SMTP_EMPTY_QUEUE arguments: @""];
}
@end