Search code examples
iosobjective-cemailios7smtp

How to send an email with SKPSMTPMessage which will parse ok in email clients?


I've been trying to use SKPSMTPMessage for an iOS application I'm creating. The app does send the emails right now but it seems there are some issues regarding email view on certain clients.

I can see the email properly on Gmail for example, but I can't do it properly for Airmail, it appears as an empty email.

Looking at the code of the email, it seems "good" for me so, I can't know what's going on.

Here is the code sent as an email.

Delivered-To: [email protected]
Received: by 10.194.33.198 with SMTP id t6csp161338wji;
        Sun, 18 May 2014 01:58:13 -0700 (PDT)
X-Received: by 10.43.138.210 with SMTP id it18mr25804198icc.23.1400403493134;
        Sun, 18 May 2014 01:58:13 -0700 (PDT)
Return-Path: <[email protected]>
Received: from o1.b99.sendgrid.net (o1.b99.sendgrid.net. [208.115.235.3])
        by mx.google.com with SMTP id au7si6505972igc.48.2014.05.18.01.58.12
        for <[email protected]>;
        Sun, 18 May 2014 01:58:13 -0700 (PDT)
Received-SPF: pass (google.com: domain of [email protected] designates 208.115.235.3 as permitted sender) client-ip=208.115.235.3;
Authentication-Results: mx.google.com;
       spf=pass (google.com: domain of [email protected] designates 208.115.235.3 as permitted sender) [email protected];
       dkim=pass [email protected]
DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sendgrid.me; 
  h=from:to:content-type:mime-version:subject:x-feedback-id; 
  s=smtpapi; bh=1gap02jh/1VKT+wxEZZuQWStvy0=; b=HFYnzTR3lNEIlBOuiy
  SLwK1Px7Lluo/tVq7wdsIa1Wyv6pbkKx4/JnPxhw3xls4WPgmyrZ8xCvbFXXFWIx
  IvXoCkZJvpg1TNqwZkpry4mW1JfQ1PTUv27lDUZGANkX1Lok7IVxUyMPuK63gOgK
  0rjCAoTALabZWrY4qSP8aVlfc=
Received: by mf182.sendgrid.net with SMTP id mf182.18991.537876231
        Sun, 18 May 2014 08:58:11 +0000 (UTC)
Received: from localhost (host-87-242-224-202.ppp.onetel.net.uk [87.242.224.202])
  by ismtpd-026 (SG) with ESMTP id 1460e8d79b4.2412.7462b
  for <[email protected]>; Sun, 18 May 2014 08:58:11 +0000 (GMT)
Date: Sun, 18 May 2014 09:58:11 +0100
Message-id: <[email protected]>
From:[email protected]
To:[email protected]
Content-Type: multipart/mixed; boundary="SKPSMTPMessage--Separator--Delimiter"
Mime-Version: 1.0
Subject:Message from Me
X-SG-EID: BJe7cohFb5FdMOzj4ylEqIH38EB2ynRmLXAsLFCZ07JGI1WXoEFGqBqvYj7/w5+f8v5gwaPbTNko4vDw1IArlAD8ChLhkRLEGh9r/jEpxrZ1qCoZXpVOneVRhNmnnBFhWydd9Rn5FzHyFx4lwLmNL28I0B8WM5OGXYGRlIrHdsw=
X-Feedback-ID: 256657:UMrjcMh3M2hIu7P3CZOEj4QTBRK+KuIuZrDj8e2//Mk=:UMrjcMh3M2hIu7P3CZOEj4QTBRK+KuIuZrDj8e2//Mk=:SG

--SKPSMTPMessage--Separator--Delimiter
Content-Transfer-Encoding: 7bit
Content-Type: text/plain; charset=UTF-8;
 format=flowed

This is a test
--SKPSMTPMessage--Separator--Delimiter


--SKPSMTPMessage--Separator--Delimiter--

Don't really know the issue. The only thing I can think of is that two delimiters are appended at the end of the email but I can't find either when is that happening.

This is the file responsible of sending the parts and, attaching the separator delimiter.

- (BOOL)sendParts
{
    NSMutableString *message = [[NSMutableString alloc] init];
    static NSString *separatorString = @"--SKPSMTPMessage--Separator--Delimiter\r\n";

    CFUUIDRef   uuidRef   = CFUUIDCreate(kCFAllocatorDefault);
    NSString    *uuid     = (NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuidRef));
    CFRelease(uuidRef);

    NSDate *now = [[NSDate alloc] init];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];

    [dateFormatter setDateFormat:@"EEE, dd MMM yyyy HH:mm:ss Z"];

    [message appendFormat:@"Date: %@\r\n", [dateFormatter stringFromDate:now]];
    [message appendFormat:@"Message-id: <%@@%@>\r\n", [(NSString *)uuid stringByReplacingOccurrencesOfString:@"-" withString:@""], self.relayHost];

    [message appendFormat:@"From:%@\r\n", fromEmail];


    if ((self.toEmail != nil) && (![self.toEmail isEqualToString:@""]))
    {
        [message appendFormat:@"To:%@\r\n", self.toEmail];
    }

    if ((self.ccEmail != nil) && (![self.ccEmail isEqualToString:@""]))
    {
        [message appendFormat:@"Cc:%@\r\n", self.ccEmail];
    }

    [message appendString:@"Content-Type: multipart/mixed; boundary=\"SKPSMTPMessage--Separator--Delimiter\"\r\n"];
    [message appendString:@"Mime-Version: 1.0\r\n"];
    [message appendFormat:@"Subject:%@\r\n\r\n",subject];
    [message appendString:separatorString];

    NSData *messageData = [message dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];

    NSLog(@"C: %s", [messageData bytes]);
    if (CFWriteStreamWriteFully((__bridge CFWriteStreamRef)outputStream, (const uint8_t *)[messageData bytes], [messageData length]) < 0)
    {
        return NO;
    }

    message = [[NSMutableString alloc] init];

    for (NSDictionary *part in parts)
    {
        // Checking it contains the actual part to avoid appending separators
        if ([part objectForKey:kSKPSMTPPartMessageKey]){
            if ([part objectForKey:kSKPSMTPPartContentDispositionKey])
            {
                [message appendFormat:@"Content-Disposition: %@\r\n", [part objectForKey:kSKPSMTPPartContentDispositionKey]];
            }
            [message appendFormat:@"Content-Transfer-Encoding: %@\r\n", [part objectForKey:kSKPSMTPPartContentTransferEncodingKey]];
            [message appendFormat:@"Content-Type: %@\r\n\r\n", [part objectForKey:kSKPSMTPPartContentTypeKey]];

            [message appendString:[part objectForKey:kSKPSMTPPartMessageKey]];
            [message appendString:@"\r\n"];

            [message appendString:separatorString];
        }
    }

    [message appendString:@"\r\n.\r\n"];

    NSLog(@"C: %@", message);
    if (CFWriteStreamWriteFully((__bridge CFWriteStreamRef)outputStream, (const uint8_t *)[message UTF8String], [message lengthOfBytesUsingEncoding:NSUTF8StringEncoding]) < 0)
    {
        return NO;
    }
    [self startLongWatchdog];
    return YES;
}

When that's logged (at NSLog(@"C: %@", message);), those two final separators aren't there.

Anyone has any hint?


Solution

  • As I supposed, the issue was that there was an additional separator that wasn't needed at all and hence the failure.

    So, on the sendParts method, I changed this:

    [message appendString:@"Content-Type: multipart/mixed; boundary=\"SKPSMTPMessage--Separator--Delimiter\"\r\n"];
    [message appendString:@"Mime-Version: 1.0\r\n"];
    [message appendFormat:@"Subject:%@\r\n\r\n",subject];
    [message appendString:separatorString];
    

    To this:

    [message appendString:@"Content-Type: multipart/mixed; boundary=\"SKPSMTPMessage--Separator--Delimiter\"\r\n"];
    [message appendString:@"Mime-Version: 1.0\r\n"];
    [message appendFormat:@"Subject:%@\r\n\r\n",subject];
    

    Basically, removing [message appendString:separatorString];

    And then, on the loop:

    for (NSDictionary *part in parts)
    {
        // Checking it contains the actual part to avoid appending separators
        if ([part objectForKey:kSKPSMTPPartMessageKey]){
            if ([part objectForKey:kSKPSMTPPartContentDispositionKey])
            {
                [message appendFormat:@"Content-Disposition: %@\r\n", [part objectForKey:kSKPSMTPPartContentDispositionKey]];
            }
    
            [message appendFormat:@"Content-Transfer-Encoding: %@\r\n", [part objectForKey:kSKPSMTPPartContentTransferEncodingKey]];
            [message appendFormat:@"Content-Type: %@\r\n\r\n", [part objectForKey:kSKPSMTPPartContentTypeKey]];
    
            [message appendString:[part objectForKey:kSKPSMTPPartMessageKey]];
            [message appendString:@"\r\n"];
    
            [message appendString:separatorString];
        }
    }
    

    I moved the separatorString to the top of the appending operation:

    for (NSDictionary *part in parts)
    {
        // Checking it contains the actual part to avoid appending separators
        if ([part objectForKey:kSKPSMTPPartMessageKey]){
    
            [message appendString:separatorString];
    
            if ([part objectForKey:kSKPSMTPPartContentDispositionKey])
            {
                [message appendFormat:@"Content-Disposition: %@\r\n", [part objectForKey:kSKPSMTPPartContentDispositionKey]];
            }
    
            [message appendFormat:@"Content-Transfer-Encoding: %@\r\n", [part objectForKey:kSKPSMTPPartContentTransferEncodingKey]];
            [message appendFormat:@"Content-Type: %@\r\n\r\n", [part objectForKey:kSKPSMTPPartContentTypeKey]];
    
            [message appendString:[part objectForKey:kSKPSMTPPartMessageKey]];
            [message appendString:@"\r\n"];
        }
    }
    

    This will solve the issue!