In a previous post I described sending email using c-client. This post takes things a little bit further and describes sending an email containing HTML content using the same c-client API.
First things first - people usually call HTML email an HTML fragment that refers inline images. These images are part of the email too and they are referred in the HTML source by their content id (using a special construct on the source reference, i.e. img src="cid:image_content_id").
Mail clients (at least Snow Leopard's Mail.app) expect the HTML fragment and the images they refer to be part of the same container. This container is a MIME multipart/related container.
The simplest HTML message that we can send is based on the following MIME multipart structure:
Our message in Mail.app:
First things first - people usually call HTML email an HTML fragment that refers inline images. These images are part of the email too and they are referred in the HTML source by their content id (using a special construct on the source reference, i.e. img src="cid:image_content_id").
Mail clients (at least Snow Leopard's Mail.app) expect the HTML fragment and the images they refer to be part of the same container. This container is a MIME multipart/related container.
The simplest HTML message that we can send is based on the following MIME multipart structure:
#include <c-client.h>
#include "c-client-interface.c"
int main()
{
#include "linkage.c"
char * hostlist[] = { "smtp.gmail.com/ssl/user=user@gmail.com/smtp", NULL };
SENDSTREAM * sndstream = smtp_open(hostlist, 0);
if(sndstream == NULL)
{
fprintf(stderr, "Can't open SMTP connection\n");
return 1;
}
BODY * body = mail_newbody();
ENVELOPE * env = mail_newenvelope();
ADDRESS * to = mail_newaddr();
env->from = mail_newaddr();
env->from->personal = strdup("User Name");
env->from->mailbox = strdup("username");
env->from->host = strdup("gmail.com");
env->subject = strdup("subject");
rfc822_parse_adrlist(&env->to, strdup("username"), strdup("destination.dom"));
char *text = (char *) fs_get (8*MAILTMPLEN);
strcpy(text, "test message \015\012\0");
#define HTMLfragment "<HTML><p>A little bit in <b>bold</b> and some <i>italic</i><br/><br/> </p><p><img src=\"cid:55665566\"></p><p>The end</p></HTML>"
/* We need three parts, a part for HTML, a part for the image and
a part that groups them (called related). */
PART * htmlpart, *imagepart, *related;
related = mail_newbody_part();
related->body.type = TYPEMULTIPART;
related->body.subtype = cpystr("related");
htmlpart = mail_newbody_part();
htmlpart->body.encoding = ENCBASE64;
htmlpart->body.contents.text.data = (char *) rfc822_binary (HTMLfragment, strlen(HTMLfragment), &htmlpart->body.contents.text.size);
htmlpart->body.parameter = mail_newbody_parameter ();
htmlpart->body.parameter->attribute = cpystr ("CHARSET");
htmlpart->body.parameter->value = cpystr ("US-ASCII");
htmlpart->body.type = TYPETEXT;
htmlpart->body.subtype = cpystr ("HTML");
htmlpart->body.description = cpystr ("html version");
//for brevity, we define a GIF file inline. in real code, you would probably load this from a file
unsigned char buf[] = {0x47, 0x49, 0x46,0x38,0x37,0x61,0x0c,0x00,0x0c,0x00,0xc2,0x04,0x00,0x00,0x24,0xff,0xff,0x00,0x00,0x00,0xa0,0x0b,0xf6,0x7a
,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x2c,0x00,0x00,0x00,0x00,0x0c,0x00,0x0c,0x00,0x00,0x03,0x23,0x18,0xba,0xd3,0xbe,0xcc,0
x35,0xa8,0xe4,0xa4,0x76,0x50,0x42,0x2c,0xe4,0x9c,0x24,0x8c,0x02,0x48,0x00,0x28,0x40,0x8e,0x5c,0x8a,0xae,0xa3,0xfb,0xc2,0xb2,0x4a,0xcb,0xb0,0x50,0x27,0x0
0,0x3b};
/* allocate a MIME part for the image content at the end of the HTML part */
imagepart = (htmlpart->next = mail_newbody_part());
/* the image is binary so we encode in base64 */
imagepart->body.encoding = ENCBASE64;
imagepart->body.contents.text.data = (char *) rfc822_binary(buf, 86, &imagepart->body.contents.text.size);
imagepart->body.type = TYPEIMAGE;
imagepart->body.subtype = cpystr ("GIF");
imagepart->body.id = cpystr("<55665566>");
/* Our body's content is of type multipart and it contains a nested part
that is in fact the related part that we have created above */
body->type = TYPEMULTIPART;
body->subtype = cpystr("alternative");
body->nested.part = mail_newbody_part ();
body->nested.part->body.contents.text.data = text;
body->nested.part->body.contents.text.size = strlen (text);
related->body.nested.part = htmlpart;
body->nested.part->next = related;
long result = smtp_mail(sndstream, "MAIL", env, body);
if(sndstream)
smtp_close(sndstream);
mail_free_envelope(&env);
mail_free_body(&body);
}
Our message in Mail.app: