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.

1084 lines
26 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. /*
  2. ** CWSMTP.m
  3. **
  4. ** Copyright (c) 2001-2007
  5. **
  6. ** Author: Ludovic Marcotte <ludovic@Sophos.ca>
  7. **
  8. ** This library is free software; you can redistribute it and/or
  9. ** modify it under the terms of the GNU Lesser General Public
  10. ** License as published by the Free Software Foundation; either
  11. ** version 2.1 of the License, or (at your option) any later version.
  12. **
  13. ** This library is distributed in the hope that it will be useful,
  14. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. ** Lesser General Public License for more details.
  17. **
  18. ** You should have received a copy of the GNU Lesser General Public
  19. ** License along with this library; if not, write to the Free Software
  20. ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. #import "CWSMTP.h"
  23. #import "CWSMTP+Protected.h"
  24. #import "CWConnection.h"
  25. #import "CWConstants.h"
  26. #import "CWInternetAddress.h"
  27. #import "Pantomime/CWMD5.h"
  28. #import <PantomimeFramework/CWMessage.h>
  29. #import "NSData+Extensions.h"
  30. #import <Foundation/NSEnumerator.h>
  31. #import <Foundation/NSNotification.h>
  32. #import "CWConnection.h"
  33. #import "CWThreadSafeArray.h"
  34. #import "CWThreadSafeData.h"
  35. #import "CWOAuthUtils.h"
  36. #import "CWService+Protected.h"
  37. #import <pEpIOSToolbox/PEPLogger.h>
  38. // The hostname/domain used to do EHLO/HELO
  39. static NSString *pEpEHLOBase = @"pretty.Easy.privacy";
  40. //
  41. // This function returns the next recipient from the array depending
  42. // if the message is redirected or not.
  43. //
  44. static inline CWInternetAddress *next_recipient(NSMutableArray *theRecipients, BOOL aBOOL)
  45. {
  46. CWInternetAddress *theAddress;
  47. NSUInteger i, count;
  48. count = [theRecipients count];
  49. for (i = 0; i < count; i++)
  50. {
  51. theAddress = [theRecipients objectAtIndex: i];
  52. if (aBOOL)
  53. {
  54. if ([theAddress type] > 3)
  55. {
  56. return theAddress;
  57. }
  58. }
  59. else
  60. {
  61. if ([theAddress type] < 4)
  62. {
  63. return theAddress;
  64. }
  65. }
  66. }
  67. return nil;
  68. }
  69. //
  70. // Private SMTP methods
  71. //
  72. @interface CWSMTP (Private)
  73. - (void) _parseAUTH_CRAM_MD5;
  74. - (void) _parseAUTH_LOGIN;
  75. - (void) _parseAUTH_LOGIN_CHALLENGE;
  76. - (void) _parseAUTH_PLAIN;
  77. - (void) _parseAUTH_OAUTH2;
  78. - (void) _parseAUTHORIZATION;
  79. - (void) _parseDATA;
  80. - (void) _parseEHLO;
  81. - (void) _parseHELO;
  82. - (void) _parseMAIL;
  83. - (void) _parseNOOP;
  84. - (void) _parseQUIT;
  85. - (void) _parseRCPT;
  86. - (void) _parseRSET;
  87. - (void) _parseServerOutput;
  88. @end
  89. //
  90. //
  91. //
  92. @implementation CWSMTP
  93. //
  94. // initializers
  95. //
  96. - (instancetype) initWithName: (NSString *) theName
  97. port: (unsigned int) thePort
  98. transport: (ConnectionTransport)transport
  99. clientCertificate: (SecIdentityRef _Nullable)clientCertificate
  100. {
  101. self = [super initWithName:theName
  102. port:thePort
  103. transport:transport
  104. clientCertificate:clientCertificate];
  105. _sent_recipients = nil;
  106. _recipients = nil;
  107. _message = nil;
  108. _data = nil;
  109. _max_size = 0;
  110. _lastCommand = SMTP_AUTHORIZATION;
  111. // We queue our first "command".
  112. [_queue addObject: AUTORELEASE([[CWSMTPQueueObject alloc] initWithCommand: _lastCommand arguments: @""])];
  113. return self;
  114. }
  115. //
  116. //
  117. //
  118. - (void) dealloc
  119. {
  120. //LogInfo(@"SMTP: -dealloc");
  121. RELEASE(_message);
  122. RELEASE(_data);
  123. RELEASE(_recipients);
  124. RELEASE(_sent_recipients);
  125. //[super dealloc];
  126. }
  127. #pragma mark - Overriden
  128. //
  129. //
  130. //
  131. - (void) cancelRequest
  132. {
  133. dispatch_sync(self.serviceQueue, ^{
  134. [super cancelRequest];
  135. });
  136. }
  137. //
  138. //
  139. //
  140. - (void) close
  141. {
  142. __weak typeof(self) weakSelf = self;
  143. dispatch_sync(self.serviceQueue, ^{
  144. typeof(self) strongSelf = weakSelf;
  145. if (strongSelf->_connected) {
  146. [strongSelf sendCommand: SMTP_QUIT arguments: @"QUIT"];
  147. }
  148. [super close];
  149. });
  150. }
  151. //
  152. // This method sends a RSET SMTP command.
  153. //
  154. - (void) reset
  155. {
  156. dispatch_sync(self.serviceQueue, ^{
  157. [self sendCommand: SMTP_RSET arguments: @"RSET"];
  158. });
  159. }
  160. //
  161. // This methods reads everything the server sends. Once it judge it has
  162. // read a full response, it calls _parseServerOutput in order to react
  163. // to what we've just received from the server.
  164. //
  165. //
  166. // RFC2821 rationale:
  167. //
  168. // An SMTP reply consists of a three digit number (transmitted as three
  169. // numeric characters) followed by some text unless specified otherwise
  170. // in this document. The number is for use by automata to determine
  171. // what state to enter next; the text is for the human user. The three
  172. // digits contain enough encoded information that the SMTP client need
  173. // not examine the text and may either discard it or pass it on to the
  174. // user, as appropriate. Exceptions are as noted elsewhere in this
  175. // document. In particular, the 220, 221, 251, 421, and 551 reply codes
  176. // are associated with message text that must be parsed and interpreted
  177. // by machines
  178. //
  179. // (...)
  180. //
  181. // The format for multiline replies requires that every line, except the
  182. // last, begin with the reply code, followed immediately by a hyphen,
  183. // "-" (also known as minus), followed by text. The last line will
  184. // begin with the reply code, followed immediately by <SP>, optionally
  185. // some text, and <CRLF>. As noted above, servers SHOULD send the <SP>
  186. // if subsequent text is not sent, but clients MUST be prepared for it
  187. // to be omitted.
  188. //
  189. // For example:
  190. //
  191. // 123-First line
  192. // 123-Second line
  193. // 123-234 text beginning with numbers
  194. // 123 The last line
  195. //
  196. //
  197. // (...)
  198. //
  199. // ... Only the EHLO, EXPN, and HELP
  200. // commands are expected to result in multiline replies in normal
  201. // circumstances, however, multiline replies are allowed for any
  202. // command.
  203. //
  204. - (void) updateRead
  205. {
  206. // Intentionally not serialized on serviceQueue. Must never been called directly by clients.
  207. NSData *aData;
  208. char *buf;
  209. NSUInteger count;
  210. //LogInfo(@"IN UPDATE READ");
  211. [super updateRead];
  212. while ((aData = [_rbuf dropFirstLine]))
  213. {
  214. [_responsesFromServer addObject: aData];
  215. buf = (char *)[aData bytes];
  216. count = [aData length];
  217. // If we got only a response code OR if we're done reading
  218. // a multiline reply, we parse the output!
  219. if (count == 3 || (count > 3 && (*(buf+3) != '-')))
  220. {
  221. [self _parseServerOutput];
  222. }
  223. }
  224. }
  225. //
  226. // This method sends a NOOP SMTP command.
  227. //
  228. - (void) noop
  229. {
  230. dispatch_sync(self.serviceQueue, ^{
  231. [self sendCommand: SMTP_NOOP arguments: @"NOOP"];
  232. });
  233. }
  234. //
  235. //
  236. //
  237. - (int) reconnect
  238. {
  239. dispatch_sync(self.serviceQueue, ^{
  240. [super reconnect];
  241. });
  242. return 0; // In case you wonder see reconnect doc: @result Pending.
  243. }
  244. //
  245. //
  246. //
  247. - (void) startTLS
  248. {
  249. dispatch_sync(self.serviceQueue, ^{
  250. [self sendCommand: SMTP_STARTTLS arguments: @"STARTTLS"];
  251. });
  252. }
  253. //
  254. // This method is used to authenticate ourself to the SMTP server.
  255. //
  256. - (void)authenticate:(NSString *)username
  257. password:(NSString *)password
  258. mechanism:(NSString *)mechanism
  259. {
  260. __weak typeof(self) weakSelf = self;
  261. dispatch_sync(self.serviceQueue, ^{
  262. __block typeof(self) strongSelf = weakSelf;
  263. strongSelf->_username = username;
  264. strongSelf->_password = password;
  265. strongSelf->_mechanism = mechanism;
  266. if (!mechanism) {
  267. AUTHENTICATION_FAILED(strongSelf->_delegate, @"");
  268. } else if ([mechanism caseInsensitiveCompare: @"PLAIN"] == NSOrderedSame) {
  269. [self sendCommand: SMTP_AUTH_PLAIN arguments: @"AUTH PLAIN"];
  270. } else if ([mechanism caseInsensitiveCompare: @"LOGIN"] == NSOrderedSame) {
  271. [self sendCommand: SMTP_AUTH_LOGIN arguments: @"AUTH LOGIN"];
  272. } else if ([mechanism caseInsensitiveCompare: @"CRAM-MD5"] == NSOrderedSame) {
  273. [self sendCommand: SMTP_AUTH_CRAM_MD5 arguments: @"AUTH CRAM-MD5"];
  274. } else if ([mechanism caseInsensitiveCompare: @"XOAUTH2"] == NSOrderedSame) {
  275. NSString *clientResponse =
  276. [CWOAuthUtils base64EncodedClientResponseForUser:strongSelf->_username
  277. accessToken:strongSelf->_password];
  278. NSString *args = [NSString stringWithFormat:@"AUTH XOAUTH2 %@", clientResponse];
  279. [self sendCommand: SMTP_AUTH_XOAUTH2 arguments: args];
  280. } else {
  281. // Unknown / Unsupported mechanism
  282. AUTHENTICATION_FAILED(strongSelf->_delegate, mechanism);
  283. }
  284. });
  285. }
  286. #pragma mark - CWTransport
  287. //
  288. // To send a message, we need its data value and the recipients at least.
  289. //
  290. // Depending on what was specified using the "set" methods, we initialize
  291. // what we really want and proceed to send the mail.
  292. //
  293. - (void) sendMessage
  294. {
  295. __weak typeof(self) weakSelf = self;
  296. dispatch_sync(self.serviceQueue, ^{
  297. typeof(self) strongSelf = weakSelf;
  298. if (!strongSelf->_message && !strongSelf->_data) {
  299. [strongSelf fail];
  300. return;
  301. }
  302. if (!strongSelf->_recipients && strongSelf->_message) {
  303. strongSelf->_recipients =
  304. [NSMutableArray arrayWithArray:[strongSelf->_message recipients]];
  305. if (!strongSelf->_data) {
  306. strongSelf->_data = [strongSelf->_message dataValue];
  307. }
  308. } else if (!strongSelf->_recipients && strongSelf->_data) {
  309. CWMessage *aMessage = [[CWMessage alloc] initWithData: strongSelf->_data];
  310. strongSelf->_message = aMessage;
  311. strongSelf->_recipients = [NSMutableArray arrayWithArray: [aMessage recipients]];
  312. }
  313. strongSelf->_sent_recipients = [strongSelf->_recipients mutableCopy];
  314. NSString *aString;
  315. // We first verify if it's a redirected message
  316. if ([strongSelf->_message resentFrom]) {
  317. strongSelf->_redirected = YES;
  318. aString = [[strongSelf->_message resentFrom] address];
  319. } else {
  320. strongSelf->_redirected = NO;
  321. aString = [[strongSelf->_message from] address];
  322. }
  323. if (strongSelf->_max_size) {
  324. [self sendCommand: SMTP_MAIL
  325. arguments: @"MAIL FROM:<%@> SIZE=%d", aString, [strongSelf->_data length]];
  326. } else {
  327. [self sendCommand: SMTP_MAIL arguments: @"MAIL FROM:<%@>", aString];
  328. }
  329. });
  330. }
  331. //
  332. //
  333. //
  334. - (void) setMessage: (CWMessage *) theMessage
  335. {
  336. __weak typeof(self) weakSelf = self;
  337. dispatch_sync(self.serviceQueue, ^{
  338. typeof(self) strongSelf = weakSelf;
  339. strongSelf->_data = nil;
  340. strongSelf->_message = theMessage;
  341. });
  342. }
  343. - (CWMessage *) message
  344. {
  345. __block CWMessage *returnee = nil;
  346. __weak typeof(self) weakSelf = self;
  347. dispatch_sync(self.serviceQueue, ^{
  348. typeof(self) strongSelf = weakSelf;
  349. returnee = strongSelf->_message;
  350. });
  351. return returnee;
  352. }
  353. //
  354. //
  355. //
  356. - (void) setMessageData: (NSData *) theData
  357. {
  358. __weak typeof(self) weakSelf = self;
  359. dispatch_sync(self.serviceQueue, ^{
  360. typeof(self) strongSelf = weakSelf;
  361. strongSelf->_message = nil;
  362. strongSelf->_data = theData;
  363. });
  364. }
  365. - (NSData *) messageData
  366. {
  367. __block NSData *returnee = nil;
  368. __weak typeof(self) weakSelf = self;
  369. dispatch_sync(self.serviceQueue, ^{
  370. typeof(self) strongSelf = weakSelf;
  371. returnee = strongSelf->_data;
  372. });
  373. return returnee;
  374. }
  375. //
  376. //
  377. //
  378. - (void) setRecipients: (NSArray *) theRecipients
  379. {
  380. __weak typeof(self) weakSelf = self;
  381. dispatch_sync(self.serviceQueue, ^{
  382. typeof(self) strongSelf = weakSelf;
  383. strongSelf->_recipients = nil;
  384. if (theRecipients) {
  385. strongSelf->_recipients = [NSMutableArray arrayWithArray: theRecipients];
  386. }
  387. });
  388. }
  389. //
  390. //
  391. //
  392. - (NSArray *) recipients
  393. {
  394. __block NSArray *returnee = nil;
  395. __weak typeof(self) weakSelf = self;
  396. dispatch_sync(self.serviceQueue, ^{
  397. typeof(self) strongSelf = weakSelf;
  398. returnee = strongSelf->_recipients;
  399. });
  400. return returnee;
  401. }
  402. @end
  403. //
  404. // Private methods
  405. //
  406. @implementation CWSMTP (Private)
  407. - (void) _parseAUTH_CRAM_MD5
  408. {
  409. NSData *aData;
  410. aData = [_responsesFromServer lastObject];
  411. if ([aData hasCPrefix: "334"])
  412. {
  413. NSString *aString;
  414. CWMD5 *aMD5;
  415. // We trim the "334 ", decode the data using base64 and we keep the challenge phrase
  416. aData = [[aData subdataFromIndex: 4] decodeBase64];
  417. aMD5 = [[CWMD5 alloc] initWithData: aData];
  418. [aMD5 computeDigest];
  419. aString = [NSString stringWithFormat: @"%@ %@", _username, [aMD5 hmacAsStringUsingPassword: _password]];
  420. [self bulkWriteData:@[[[aString dataUsingEncoding: _defaultCStringEncoding] encodeBase64WithLineLength: 0],
  421. _crlf]];
  422. RELEASE(aMD5);
  423. }
  424. else if ([aData hasCPrefix: "235"])
  425. {
  426. AUTHENTICATION_COMPLETED(_delegate, @"CRAM-MD5");
  427. }
  428. else
  429. {
  430. AUTHENTICATION_FAILED(_delegate, @"CRAM-MD5");
  431. }
  432. }
  433. //
  434. //
  435. //
  436. - (void) _parseAUTH_LOGIN
  437. {
  438. NSData *aData;
  439. aData = [_responsesFromServer lastObject];
  440. if ([aData hasCPrefix: "334"])
  441. {
  442. NSString *aString;
  443. aString = [[NSString alloc] initWithData: [[_username dataUsingEncoding: _defaultCStringEncoding] encodeBase64WithLineLength: 0]
  444. encoding: _defaultCStringEncoding];
  445. [self sendCommand: SMTP_AUTH_LOGIN_CHALLENGE arguments: aString];
  446. RELEASE(aString);
  447. }
  448. else
  449. {
  450. AUTHENTICATION_FAILED(_delegate, @"LOGIN");
  451. }
  452. }
  453. //
  454. //! rename if needed.
  455. //
  456. - (void) _parseAUTH_LOGIN_CHALLENGE
  457. {
  458. NSData *aData;
  459. aData = [_responsesFromServer lastObject];
  460. if ([aData hasCPrefix: "334"])
  461. {
  462. NSString *aString;
  463. aString = [[NSString alloc] initWithData: [[_password dataUsingEncoding: _defaultCStringEncoding] encodeBase64WithLineLength: 0]
  464. encoding: _defaultCStringEncoding];
  465. [self sendCommand: SMTP_AUTH_LOGIN_CHALLENGE arguments: aString];
  466. RELEASE(aString);
  467. }
  468. else if ([aData hasCPrefix: "235"])
  469. {
  470. AUTHENTICATION_COMPLETED(_delegate, @"LOGIN");
  471. }
  472. else
  473. {
  474. LogInfo(@"Authentification response: |%@|",
  475. [aData asciiString]);
  476. AUTHENTICATION_FAILED(_delegate, @"LOGIN");
  477. }
  478. }
  479. //
  480. //
  481. //
  482. - (void) _parseAUTH_PLAIN
  483. {
  484. NSData *aData;
  485. aData = [_responsesFromServer lastObject];
  486. if ([aData hasCPrefix: "334"])
  487. {
  488. NSMutableData *aMutableData;
  489. NSUInteger len_username, len_password;
  490. len_username = [_username length];
  491. if (!_password)
  492. {
  493. len_password = 0;
  494. }
  495. else
  496. {
  497. len_password = [_password length];
  498. }
  499. // We create our phrase
  500. aMutableData = [NSMutableData dataWithLength: (len_username + len_password + 2)];
  501. [aMutableData replaceBytesInRange: NSMakeRange(1,len_username)
  502. withBytes: [[_username dataUsingEncoding: _defaultCStringEncoding] bytes]];
  503. [aMutableData replaceBytesInRange: NSMakeRange(2 + len_username, len_password)
  504. withBytes: [[_password dataUsingEncoding: [NSString defaultCStringEncoding]] bytes]];
  505. [self bulkWriteData:@[[aMutableData encodeBase64WithLineLength: 0],
  506. _crlf]];
  507. }
  508. else if ([aData hasCPrefix: "235"])
  509. {
  510. AUTHENTICATION_COMPLETED(_delegate, @"PLAIN");
  511. }
  512. else
  513. {
  514. AUTHENTICATION_FAILED(_delegate, @"PLAIN");
  515. }
  516. }
  517. //
  518. //
  519. //
  520. - (void) _parseAUTH_OAUTH2
  521. {
  522. /*
  523. Example gmail (linebreak not included):
  524. C: AUTH XOAUTH2 dXNlcj1zb21ldXNlckBleGFtcGxlLmNvbQFhdXRoPUJlYXJl
  525. ciB2RjlkZnQ0cW1UYzJOdmIzUmxja0JoZEhSaGRtbHpkR0V1WTI5dENnPT0BAQ==
  526. S: 235 2.7.0 Accepted
  527. */
  528. NSData *aData = [_responsesFromServer lastObject];
  529. if ([aData hasCPrefix: "235"])
  530. {
  531. AUTHENTICATION_COMPLETED(_delegate, @"XOAUTH2");
  532. }
  533. else
  534. {
  535. AUTHENTICATION_FAILED(_delegate, @"XOAUTH2");
  536. }
  537. }
  538. //
  539. //
  540. //
  541. - (void) _parseAUTHORIZATION
  542. {
  543. NSData *aData;
  544. aData = [_responsesFromServer lastObject];
  545. // 220 <domain> Service ready
  546. if ([aData hasCPrefix: "220"])
  547. {
  548. [self sendCommand: SMTP_EHLO arguments: [NSString stringWithFormat:@"EHLO %@", pEpEHLOBase]];
  549. }
  550. else
  551. {
  552. // Handle the fact when a server is loaded and can't handle our requests
  553. // right away.
  554. //! unhandled
  555. }
  556. }
  557. //
  558. //
  559. //
  560. - (void) _parseDATA
  561. {
  562. NSData *aData;
  563. aData = [_responsesFromServer lastObject];
  564. // If we can proceed to write the message's data, let's do so.
  565. if ([aData hasCPrefix: "354"])
  566. {
  567. NSMutableData *aMutableData;
  568. NSRange r1, r2;
  569. // We first replace all occurences of LF by CRLF in the Message's data.
  570. //
  571. aMutableData = [[NSMutableData dataWithData: _data] replaceLFWithCRLF];
  572. _data = nil; // save memory by deleting as soon as possible
  573. //
  574. // According to RFC 2821 section 4.5.2, we must check for the character
  575. // sequence "<CRLF>.<CRLF>"; any occurrence have its period duplicated
  576. // to avoid data transparency.
  577. //
  578. r1 = [aMutableData rangeOfCString: "\r\n."];
  579. while (r1.location != NSNotFound)
  580. {
  581. [aMutableData replaceBytesInRange: r1 withBytes: "\r\n.." length: 4];
  582. r1 = [aMutableData rangeOfCString: "\r\n."
  583. options: 0
  584. range: NSMakeRange(NSMaxRange(r1)+1, [aMutableData length]-NSMaxRange(r1)-1)];
  585. }
  586. //
  587. // We now look for the Bcc: header. If it is present, we remove it.
  588. // Some servers, like qmail, do not remove it automatically.
  589. //
  590. r1 = [aMutableData rangeOfCString: "\r\n\r\n"];
  591. r1 = [aMutableData rangeOfCString: "\r\nBcc: "
  592. options: 0
  593. range: NSMakeRange(0,r1.location-1)];
  594. if (r1.location != NSNotFound)
  595. {
  596. // We search for the first \r\n AFTER the Bcc: header and
  597. // replace the whole thing with \r\n.
  598. r2 = [aMutableData rangeOfCString: "\r\n"
  599. options: 0
  600. range: NSMakeRange(NSMaxRange(r1)+1,[aMutableData length]-NSMaxRange(r1)-1)];
  601. [aMutableData replaceBytesInRange: NSMakeRange(r1.location, NSMaxRange(r2)-r1.location)
  602. withBytes: "\r\n"
  603. length: 2];
  604. }
  605. [self bulkWriteData:@[aMutableData,
  606. [NSData dataWithBytes: "\r\n.\r\n" length: 5]]];
  607. }
  608. else if ([aData hasCPrefix: "250"])
  609. {
  610. // The data we wrote in the previous call was sucessfully written.
  611. // We inform the delegate that the mail was sucessfully sent.
  612. PERFORM_SELECTOR_2(_delegate, @selector(messageSent:), PantomimeMessageSent, _message, @"Message");
  613. // Delete memory consuming objects as soon as possible
  614. _data = nil;
  615. _message = nil;
  616. }
  617. else
  618. {
  619. [self fail];
  620. }
  621. }
  622. //
  623. //
  624. //
  625. - (void) _parseEHLO
  626. {
  627. NSData *aData;
  628. NSUInteger i, count;
  629. count = [_responsesFromServer count];
  630. for (i = 0; i < count; i++)
  631. {
  632. aData = [_responsesFromServer objectAtIndex: i];
  633. if ([aData hasCPrefix: "250"])
  634. {
  635. // We parse the SMTP service extensions. For now, we support the SIZE
  636. // and the AUTH extensions. We ignore the rest.
  637. aData = [aData subdataFromIndex: 4];
  638. // We add it to our capabilities
  639. [_capabilities addObject: AUTORELEASE([[NSString alloc] initWithData: aData encoding: _defaultCStringEncoding])];
  640. // Example of responses:
  641. //
  642. // AUTH LOGIN
  643. // AUTH=PLAIN CRAM-MD5 DIGEST-MD5
  644. //
  645. if ([aData hasCPrefix: "AUTH"])
  646. {
  647. NSEnumerator *theEnumerator;
  648. id aString;
  649. // We chomp the "AUTH " or "AUTH=" part and we decode our
  650. // supported mechanisms.
  651. theEnumerator = [[[aData subdataFromIndex: 5] componentsSeparatedByCString: " "] objectEnumerator];
  652. while ((aString = [theEnumerator nextObject]))
  653. {
  654. aString = [aString asciiString];
  655. if (![_supportedMechanisms containsObject: aString])
  656. {
  657. [_supportedMechanisms addObject: aString];
  658. }
  659. }
  660. }
  661. //
  662. // SIZE size-param
  663. // size-param ::= [1*DIGIT]
  664. //
  665. // See RFC1870 for detailed information.
  666. //
  667. else if ([aData hasCPrefix: "SIZE"])
  668. {
  669. NSRange aRange;
  670. // We must be careful here. Some broken servers will send only
  671. // 250-SIZE
  672. // and we don't want to parse an inexistant value.
  673. aRange = [aData rangeOfCString: " "];
  674. if (aRange.length)
  675. {
  676. _max_size = atoi([[aData subdataFromIndex: aRange.location+1] cString]);
  677. }
  678. }
  679. }
  680. else
  681. {
  682. // The server doesn't handle EHLO. We send it
  683. // a HELO greeting instead.
  684. [self sendCommand: SMTP_HELO arguments: [NSString stringWithFormat:@"HELO %@", pEpEHLOBase]];
  685. break;
  686. }
  687. }
  688. //! - Inform the delegate if it is ready or not, especially if EHLO failed
  689. PERFORM_SELECTOR_1(_delegate, @selector(serviceInitialized:), PantomimeServiceInitialized);
  690. }
  691. //
  692. //
  693. //
  694. - (void) _parseHELO
  695. {
  696. //! - Implement. + inform the delegate if it's ready or not.
  697. }
  698. //
  699. // This method parses the result received from the server
  700. // after issuing a "MAIL FROM: <>" command.
  701. //
  702. // If the result is successful, we proceed by sending the first RCPT.
  703. //
  704. - (void) _parseMAIL
  705. {
  706. NSData *aData;
  707. aData = [_responsesFromServer lastObject];
  708. if ([aData hasCPrefix: "250"])
  709. {
  710. // We write the first recipient while respecting the fact
  711. // that we are bouncing or not the message.
  712. PERFORM_SELECTOR_1(_delegate, @selector(transactionInitiationCompleted:), PantomimeTransactionInitiationCompleted);
  713. [self sendCommand: SMTP_RCPT arguments: @"RCPT TO:<%@>", [next_recipient(_sent_recipients, _redirected) address]];
  714. }
  715. else
  716. {
  717. if (!PERFORM_SELECTOR_1(_delegate, @selector(transactionInitiationFailed:), PantomimeTransactionInitiationFailed))
  718. {
  719. [self fail];
  720. }
  721. }
  722. }
  723. //
  724. //
  725. //
  726. - (void) _parseNOOP
  727. {
  728. // Do what?
  729. }
  730. //
  731. //
  732. //
  733. - (void) _parseQUIT
  734. {
  735. NSData *aData;
  736. aData = [_responsesFromServer lastObject];
  737. if ([aData hasCPrefix: "221"])
  738. {
  739. // Do anything special here?
  740. }
  741. [super close];
  742. }
  743. //
  744. // This method is invoked everytime we sent a recipient to the
  745. // server using the RCPT command.
  746. //
  747. // If it was successful, this command sends the next one, if any
  748. // by first removing the previously sent one from _recipients.
  749. //
  750. - (void) _parseRCPT
  751. {
  752. NSData *aData;
  753. aData = [_responsesFromServer lastObject];
  754. if ([aData hasCPrefix: "250"])
  755. {
  756. CWInternetAddress *theAddress;
  757. theAddress = next_recipient(_sent_recipients, _redirected);
  758. if (theAddress)
  759. {
  760. [_sent_recipients removeObject: theAddress];
  761. theAddress = next_recipient(_sent_recipients, _redirected);
  762. if (theAddress)
  763. {
  764. [self sendCommand: SMTP_RCPT arguments: @"RCPT TO:<%@>", [theAddress address]];
  765. return;
  766. }
  767. }
  768. // We are done writing the recipients, we now write the content
  769. // of the message.
  770. PERFORM_SELECTOR_2(_delegate, @selector(recipientIdentificationCompleted:), PantomimeRecipientIdentificationCompleted, _recipients, @"Recipients");
  771. [self sendCommand: SMTP_DATA arguments: @"DATA"];
  772. }
  773. else
  774. {
  775. if (!PERFORM_SELECTOR_1(_delegate, @selector(recipientIdentificationFailed:), PantomimeRecipientIdentificationFailed))
  776. {
  777. [self fail];
  778. }
  779. }
  780. }
  781. //
  782. //
  783. //
  784. - (void) _parseRSET
  785. {
  786. NSData *aData;
  787. aData = [_responsesFromServer lastObject];
  788. if ([aData hasCPrefix: "250"])
  789. {
  790. PERFORM_SELECTOR_1(_delegate, @selector(transactionResetCompleted:), PantomimeTransactionResetCompleted);
  791. }
  792. else
  793. {
  794. PERFORM_SELECTOR_1(_delegate, @selector(transactionResetFailed:), PantomimeTransactionResetFailed);
  795. }
  796. }
  797. //
  798. //
  799. //
  800. - (void) _parseSTARTTLS
  801. {
  802. NSData *aData = [_responsesFromServer lastObject];
  803. if ([aData hasCPrefix: "220"]) {
  804. // We first activate SSL.
  805. [(id<CWConnection>) _connection startTLS];
  806. // We now forget about the initial negotiated state; see RFC2487 for more details,
  807. [_supportedMechanisms removeAllObjects];
  808. [self sendCommand: SMTP_EHLO
  809. arguments: [NSString stringWithFormat:@"EHLO %@", pEpEHLOBase]];
  810. } else {
  811. // The server probably doesn't support TLS. We inform the delegate that the transaction
  812. // initiation failed or that the message wasn't sent.
  813. if (!PERFORM_SELECTOR_1(_delegate, @selector(transactionInitiationFailed:),
  814. PantomimeTransactionInitiationFailed)) {
  815. [self fail];
  816. }
  817. }
  818. }
  819. //
  820. //
  821. //
  822. - (void) _parseServerOutput
  823. {
  824. NSData *aData;
  825. if (![_responsesFromServer count])
  826. {
  827. return;
  828. }
  829. // We read only the first response. The _parseXYZ methods
  830. // will handle multiline responses.
  831. aData = [_responsesFromServer objectAtIndex: 0];
  832. if ([aData hasCPrefix: "421"])
  833. {
  834. //! - lost connection
  835. //LogInfo(@"LOST CONNECTION TO THE SERVER");
  836. [super close];
  837. }
  838. else
  839. {
  840. switch (_lastCommand)
  841. {
  842. case SMTP_AUTH_CRAM_MD5:
  843. [self _parseAUTH_CRAM_MD5];
  844. break;
  845. case SMTP_AUTH_LOGIN:
  846. [self _parseAUTH_LOGIN];
  847. break;
  848. case SMTP_AUTH_LOGIN_CHALLENGE:
  849. [self _parseAUTH_LOGIN_CHALLENGE];
  850. break;
  851. case SMTP_AUTH_PLAIN:
  852. [self _parseAUTH_PLAIN];
  853. break;
  854. case SMTP_AUTH_XOAUTH2:
  855. [self _parseAUTH_OAUTH2];
  856. break;
  857. case SMTP_DATA:
  858. [self _parseDATA];
  859. break;
  860. case SMTP_EHLO:
  861. [self _parseEHLO];
  862. break;
  863. case SMTP_HELO:
  864. [self _parseHELO];
  865. break;
  866. case SMTP_MAIL:
  867. [self _parseMAIL];
  868. break;
  869. case SMTP_NOOP:
  870. [self _parseNOOP];
  871. break;
  872. case SMTP_QUIT:
  873. [self _parseQUIT];
  874. break;
  875. case SMTP_RCPT:
  876. [self _parseRCPT];
  877. break;
  878. case SMTP_RSET:
  879. [self _parseRSET];
  880. break;
  881. case SMTP_STARTTLS:
  882. [self _parseSTARTTLS];
  883. break;
  884. case SMTP_AUTHORIZATION:
  885. [self _parseAUTHORIZATION];
  886. break;
  887. default:
  888. break;
  889. //!
  890. }
  891. }
  892. // We are done parsing this entry...
  893. [_responsesFromServer removeAllObjects];
  894. // We remove the last object of the queue....
  895. if ([_queue lastObject])
  896. {
  897. [_queue removeLastObject];
  898. }
  899. [self sendCommand: SMTP_EMPTY_QUEUE arguments: @""];
  900. }
  901. @end