Sending mail on Snow Leopard using the c-client API

c-client is an API that you can use to build email clients and servers.
It's been around for more then 10 years and it implements virtually anything you would want to do with SMTP, IMAP or POP, albeit in ANSI C which makes things a bit more challenging, especially when you go through the sources and try to follow the pointer arithmetic being used.

I haven't touched c-client since 2001 (that's 8 years ago) and I've run into a pleasant surprise finding c-client alive and well in 2009, and compiling "out of the box" on Snow Leopard on 64 bit. Many thanks, Mark Crispin.

Here's how you write a small application that acts as a SMTP client.

1/ Download the distribution (ftp://ftp.cac.washington.edu/imap/imap.tar.Z)

2/ Un-archive the distribution

3/ Build the distribution
cd imap-2007e/
make osx


4/ Build a small application that sends an SMTP message

c-client requires an application linking against it to implement a set of functions.
These are grouped in our example under the file called c-client-interface.c:


// --------------------- c-client-interface.c --------------------------------
#include <stdio.h>

void mm_searched (MAILSTREAM *stream,unsigned long number) {}
void mm_exists (MAILSTREAM *stream,unsigned long number) {}
void mm_expunged (MAILSTREAM *stream,unsigned long number){}
void mm_flags (MAILSTREAM *stream,unsigned long number) {}
void mm_notify (MAILSTREAM *stream,char *string,long errflg) {}
void mm_list (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes) {}
void mm_lsub (MAILSTREAM *stream,int delimiter,char *mailbox,long attributes) {}
void mm_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status) {}

void mm_log (char *string,long errflg)
{
fprintf(stdout, "mm_log: %s\n", string);
}

void mm_dlog (char *string)
{
fprintf(stdout, "mm_dlog: %s\n", string);
}

void mm_login (NETMBX *mb,char *user,char *pwd,long trial)
{
fprintf(stdout, "mm_login for mb->user: %s\n", mb->user);
strcpy(user, mb->user);
strcpy(pwd, "passwordGoesHere");
}

void mm_critical (MAILSTREAM *stream) {}
void mm_nocritical (MAILSTREAM *stream) {}
long mm_diskerror (MAILSTREAM *stream,long errcode,long serious) {}
void mm_fatal (char *string) {}



We implement only three functions from the interface, namely mm_log and mm_dlog for logging purposes and mm_login, required to provide the password for our SMTP authentication when the server requires it.

The application itself is as basic as it gets. Note the host specification (it contains "novalidate-cert" because the certificate on our mail server is invalid).
The host specification is used by the smtp_open call that takes care of all the hard work that goes behind the scenes, that is the negotiation in between c-client and the SMTP server.


// --------------------- test.c ------------------------------------
#include <c-client.h>

#include "c-client-interface.c"
int main()
{
#include "linkage.c"

char * hostlist[] = { "mail.somedomain.com/ssl/novalidate-cert/user=senderUserName", 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();

// data about the sender
env->from = mail_newaddr();
env->from->personal = strdup("Sender Full Name");
env->from->mailbox = strdup("senderUserName");
env->from->host = strdup("somedomain.com");

// subject for our message
env->subject = strdup("subject");

// destination for our message (the recipient)
rfc822_parse_adrlist(&env->to, strdup("recipientUserName"), strdup("destinationdomain.com"));

// the content of our message
body->type = TYPETEXT;
char *text = (char *) fs_get (8*MAILTMPLEN);
strcpy(text, "test message \015\012\0");
body->contents.text.data = text;
body->contents.text.size = strlen(text);



long result = smtp_mail(sndstream, "MAIL", env, body);

if(sndstream)
smtp_close(sndstream);

mail_free_envelope(&env);
mail_free_body(&body);
}




To build the application, once you saved the c-client-interface.c and the test.c files, you need to run (assuming you've unarchived the imap archive on your Desktop):

gcc test.c -L ~/Desktop/imap-2007e/c-client/c-client.a -I ~/Desktop/imap-2007e/c-client -lcrypto -lssl -lkrb4


Oh, and if you compile on Snow Leopard you will get a 64 bit application:


cristi:c-client diciu$ file ./a.out
./a.out: Mach-O 64-bit executable x86_64