header

Torsten Curdt’s weblog

Sending emails from Cocoa

With 10.5 Apple deprecated the use of NSMailDelivery – without a replacement. So the questions comes up regulary – how does one send emails from Cocoa? Turns out there are couple of frameworks available that can also be used to do the job.

EdMessage has been around for ages. Checkout the example on how to use the API. MailCore is also really easy to use. But both lack support for asynchronous mail delivery. With MailCore I couldn’t even work out how to send attachments. Also no idea if it’s getting developed anymore. So if you are OK to use a LGPL library Pantomime seemsseemed like a good choice. Sending simple emails is quite straight forward.


CWMessage *message = [[CWMessage alloc] init];

CWInternetAddress *address;

address = [[CWInternetAddress alloc] initWithString:@"[email protected]"];
[message setFrom:address];
[address release];

address = [[CWInternetAddress alloc] initWithString:@"[email protected]"];
[address setType:PantomimeToRecipient];
[message addRecipient:address];
[address release];

[message setSubject:@"test"];

[message setContentType: @"text/plain"];
[message setContentTransferEncoding: PantomimeEncodingNone];
[message setCharset: @"us-ascii"];

[message setContent: [@"This is a simple content." dataUsingEncoding: NSASCIIStringEncoding]];

smtp = [[CWSMTP alloc] initWithName:@"smtp.gmail.com" port:465];
[smtp setDelegate: self];
[smtp setMessage: message];
[message release];

ssl = YES;
mechanism = @"PLAIN";

[smtp connectInBackgroundAndNotify];

Pantomime features an asynchronous connection handling and sends messages to the delegate. The following methods should be implemented:



- (void) authenticationCompleted: (NSNotification *) theNotification
{
    NSLog(@"Authentication completed! Sending the message.");
    [smtp sendMessage];
}

- (void) authenticationFailed: (NSNotification *) theNotification
{
    NSLog(@"Authentication failed! Closing the connection.");
    [smtp close];
}

- (void) connectionEstablished: (NSNotification *) theNotification
{
    NSLog(@"Connected!");

    if (ssl) {
        NSLog(@"Now starting SSL...");
        [(CWTCPConnection *)[smtp connection] startSSL];
    }
}

- (void) connectionTerminated: (NSNotification *) theNotification
{
    NSLog(@"Connection closed.");
}

- (void) messageSent: (NSNotification *) theNotification
{
    NSLog(@"Sent! Closing the connection.");
    [smtp close];
}

- (void) serviceInitialized: (NSNotification *) theNotification
{
    if (ssl) {
        NSLog(@"SSL handshaking completed.");
    }

    if ([mechanism isEqualToString: @"NONE"]) {
        NSLog(@"Sending the message.");
        [smtp sendMessage];
    } else {
        NSLog(@"Available authentication mechanisms: %@", [smtp supportedMechanisms]);
        [smtp authenticate:@"username"  password:@"secret"  mechanism: mechanism];
    }
}

- (void) transactionResetCompleted: (NSNotification *) theNotification
{
    NSLog(@"Sending the message over the same connection.");
    [smtp sendMessage];
}

That’s a little more verbose than the simple synchronous delivery but it gives you a lot more flexibility to deal with authentication and connection problems.

As always sending mails with attachments requires a bit more work during the message setup. You basically need to wrap all the content in a multi part message like this:


...
[message setSubject:@"subject"];

CWMIMEMultipart *multipart = [[CWMIMEMultipart alloc] init];
CWPart *part = [[CWPart alloc] init];

[part setContentType: @"text/plain"];
[part setContentTransferEncoding: PantomimeEncodingQuotedPrintable];
[part setFormat: PantomimeFormatUnknown];
[part setCharset: @"UTF-8"];
[part setContent: [@"text body" dataUsingEncoding:NSUTF8StringEncoding]];

[multipart addPart:part];
[part release];
part = [[CWPart alloc] init];

NSFileWrapper *file = [[NSFileWrapper alloc] initWithPath:@"/path/to/file"];
[part setFilename: [[file filename] lastPathComponent]];
[part setContentType: @"application/octet-stream"];
[part setContentTransferEncoding: PantomimeEncodingBase64];
[part setContentDisposition: PantomimeAttachmentDisposition];
[part setContent: [file regularFileContents]];

[multipart addPart:part];
[part release];
[file release];

[message setContentTransferEncoding: PantomimeEncodingNone];
[message setContentType: @"multipart/mixed"];
[message setContent: multipart];

[multipart release];

[message setBoundary: [CWMIMEUtility globallyUniqueBoundary]];

[smtp setMessage: message];

NSLog(@"Sending message");

[smtp connectInBackgroundAndNotify];

In order to get to the Mail.app SMTP settings you can “steal” them directly from the Mail.app preferences


NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(
         NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryPath = [searchPaths objectAtIndex:0];
NSString *mailPrefsPath = [[libraryPath stringByAppendingPathComponent:@"Preferences"] stringByAppendingPathComponent:@"com.apple.mail.plist"];

NSDictionary *mailPreferences = [NSDictionary dictionaryWithContentsOfFile:mailPrefsPath];

NSArray *accounts = [mailPreferences valueForKey:@"DeliveryAccounts"];

Quite a bit of code just to get around the deprecation. Another alternative is to go through the new scripting bridge. But I personally consider that as a terrible hack. So I will stick with the above. Hope this little write-up is helpful!

Update: Turns out EdMessage is currently been worked on. Unfortunately no ETA and the current sources are not properly building.

Update: A new version of EdMessage has been released with lots of fixes and enhancements. I’ve also run into some nasty problems with Pantomime. So in the end I’ve switched to EdMessage and haven’t looked back. The official release is 10.5 only, but you can find my version that also compiles on 10.4 over on github.

  • Sam
    Hi guys, i'm currently working on an in app mailing on iPhone app and poking around led me here, i've picked EDMessage to work on but seems like it has yet support iPhone.

    Any guide/example to make EDMessage to work on iPhone?

    Or any links to a modified EDMessage for the iPhone?

    Any info on these will really help. Thanks!
  • Chris
    OK, so how do you steal Mail.app passwords? At least on my machine, gmail smtp server passwords seem to be in the keychain, and I can readily steal them, but mobile-me, smtp.me.com etc passwords don't appear to be in the keychain nor in com.apple.mail.plist. I'm assuming Mail.app put the gmail smtp settings in my keychain, because at least some of the entries list Mail.app as being able to access them, but I have no idea why other smtp servers are not there, or where they are stored.
  • Rob
    I would love to use it, I have the same problem as you. I need a opensource framework for pop3 and imap.
  • You would probably have to add that to EDMessage. Don't think there is a callback delegate yet. See EDMailAgent.m
  • I'm using EDMessage to add email support to my app.
    but how do I track the upload progress?
  • No, I didn't have to create a cert. (Why would you?) And I am pretty sure I just linked against the standard SSL library. It's been a while but IIRC it worked quite straight forward. Sorry.
  • @Andrew, how do I email you? I would like the code. My email is yes--at--gaertners.org

    @tcurdt, I just have a bunch of customizations and code written around pantomime. It's working fine for years now, but now I need to implement SSL. I wanted to know if you need to make a certificate (how), which library version you used, where did you place it in the folders (the code calls for openssl/ssl.h, but that's not the regular way openssl comes in), if you had to compile openssl before linking to it, how you mixed C and Objective-C. etc.

    It would be much appreciated..
  • @Andrew: What about just throwing it up on github so we can easily help out with the polishing?

    @Marco: Really wondering about the "need" to use Pantomime. But the above code also worked just fine for me with SSL. Just make sure it's also really a SMTP over SSL or a TLS server answering on the port you provide. IIRC Pantomime wasn't handling it too well if it wasn't the case and you tried SSL anyway.
  • Referring to my above mention of an open-source solution, anyone please feel free to email me for the code. I'm too lazy at the moment to really package it for public distro, but it's a single class that only needs CocoaAsyncSocket. Supports gmail SMTP, STARTTLS + direct SSL, but pretty basic in general.
  • Hi! I know you've moved away from Pantomime, but I really need to use it. It's working great right now, with the exception of SSL. Would you be a great savior and provide just a few hints on how to do it? I can't find the info anywhere.
  • @Mark: Yes, I got that working fine. But I ended up having other problems with Pantomime. (Crashes!) So I am also in the middle of moving away from Pantomime.

    There is a new release of EDMessage. Unfortunately it was not building for 10.4. So I fixed that an put a Tiger/Leopard compatible version up on github. It still does not have asynchronous sending but I guess I will just wrap it in a NSThread for now.
  • Do you really have SSL working in Pantomime? We could use some help, as we can't seem to make SSL work. Everything else is working great, with the occasional hiccup. It is going into our next release and we need to support SSL, as many of our users require secure connections.

    Any assistance will be greatly appreciated!
  • EDMessage has been re-released recently and now works fine on 10.5 (Leopard). Some of the documentation hasn't been updated yet so it is worthwhile looking at the headers files if in doubt.

    The main mail sending class EDMailAgent does not use a delegate, it can be configured with all necessary details upfront. If you need custom behaviour, it might be easiest to use a subclass of EDMailAgent.
  • Mahaboob
    I have been developed an email application using Pantomime framework and it was worked well. But, now it stopped working. It is only firing the events connectionEstablished: and serviceInitialized: then it is not going further.
    How can I solve this?
  • Any ideas on sending emails in Cocoa in the iPhone SDK?

    I'm writing my own that is compatible with the SDK limitations, and am open to open-sourcing it. Can't talk about what the limitations are (for the moment), but as the code works on Mac OS X too, I don't see why I can't release it.
  • Thanks for the feedback, David. Looking forward for the new code to arrive. Cheers.
  • David
    - EdMessages' proper name is EDMessage

    - EDMessage as it exists on the referenced site will not build. I have sustantially reworked the project and added authentication, but I have no way to deliver this new code at this point - stay tuned

    EDMessage is somewhat easier to use than Pantomime, but does not send asynchronously (guess I should add that :-)
blog comments powered by Disqus