Tiny SMTP Client

Dependencies:   EthernetNetIf mbed

TinySMTP.cpp

Committer:
okini3939
Date:
2011-07-28
Revision:
1:05064fe7ea1e
Parent:
0:7440c9b170aa

File content as of revision 1:05064fe7ea1e:

/*
 * mbed Tiny SMTP Client
 * Copyright (c) 2011 Hiroshi Suga
 * Released under the MIT License: http://mbed.org/license/mit
 */

/** @file
 * @brief Tiny SMTP Client
 */

#include "mbed.h"
#include "EthernetNetIf.h"
#include "TCPSocket.h"
#include "DNSRequest.h"
#include "TinySMTP.h"

#define STATUS_NONE 0
#define STATUS_READABLE 1
#define STATUS_CONNECTED 2
#define STATUS_ERROR 3
#define STATUS_DISCONNECTED 4

static TCPSocket *smtp;
static volatile int tcp_ready, tcp_readable, tcp_writable;
static volatile int dns_status;


// Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
int base64enc(const char *input, unsigned int length, char *output, int outputlen) {
  static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  unsigned int c, c1, c2, c3;

  if (outputlen < (((length-1)/3)+1)<<2) return -1;

  for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) {
    c1 = ((((unsigned char)*((unsigned char *)&input[i]))));
    c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0;
    c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0;

    c = ((c1 & 0xFC) >> 2);
    output[j+0] = base64[c];
    c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4);
    output[j+1] = base64[c];
    c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6);
    output[j+2] = (length>i+1)?base64[c]:'=';
    c = (c3 & 0x3F);
    output[j+3] = (length>i+2)?base64[c]:'=';
  }
  output[(((length-1)/3)+1)<<2] = '\0';
  return 0;
}


void isr_smtp (TCPSocketEvent e) {

#ifdef DEBUG
    printf("tcp(%d)\r\n", e);
#endif
    switch(e) {
    case TCPSOCKET_CONNECTED:
        tcp_ready = 1;
        break;

    case TCPSOCKET_READABLE: //Incoming data
        tcp_readable = 1;
        break;

    case TCPSOCKET_WRITEABLE: //We can send data
        tcp_writable = 1;
        break;

    case TCPSOCKET_CONTIMEOUT:
    case TCPSOCKET_CONRST:
    case TCPSOCKET_CONABRT:
    case TCPSOCKET_ERROR:
    case TCPSOCKET_DISCONNECTED:
        tcp_ready = 0;
        break;
    }
}

int wait_smtp (int code) {
    Timer timeout;
    int i;
    char buf[1500];

    // wait responce
    timeout.reset();
    timeout.start();
    while (timeout.read_ms() < SMTP_TIMEOUT) {
        if (tcp_readable) break;
        Net::poll();
    }
    timeout.stop();
    if (! tcp_readable) return -1;
    // recv
    i = smtp->recv(buf, sizeof(buf));
    if (i < sizeof(buf) - 1) tcp_readable = 0;
    buf[i] = 0;
#ifdef DEBUG
    printf(buf);
#endif

    // check return code
    if (atoi(buf) == code) return 0;

    return -1;
}

void isr_dns (DNSReply r) {

#ifdef DEBUG
    printf("dns(%d)\r\n", r);
#endif
    if (DNS_FOUND) {
        dns_status = 1;
    } else {
        dns_status = -1;
    }
}

int sendmail (char *to, char *from, char *data, Host *host, char *user, char *pwd) {
    TCPSocketErr err;
    Timer timeout;
    int ret = -1;

    smtp = new TCPSocket;
    tcp_ready = 0;
    tcp_readable = 0;
    tcp_writable = 0;

    smtp->setOnEvent(isr_smtp);

    // connect
    if (host->getIp().isNull()) {
        DNSRequest dns;
        dns_status = 0;
        dns.setOnReply(isr_dns);
        if (dns.resolve(host) != DNS_OK) goto exit;
        timeout.reset();
        timeout.start();
        while (timeout.read_ms() < SMTP_TIMEOUT) {
            if (dns_status) break;
            Net::poll();
        }
        timeout.stop();
        if (dns_status <= 0) goto exit;
#ifdef DEBUG
        printf("%s [%d.%d.%d.%d]\r\n", host->getName(), (unsigned char)host->getIp()[0], (unsigned char)host->getIp()[1], (unsigned char)host->getIp()[2], (unsigned char)host->getIp()[3]);
#endif
    }
    if (! host->getPort()) {
        host->setPort(SMTP_PORT);
    }

    err = smtp->connect(*host);
    if (err != TCPSOCKET_OK) goto exit;

    // wait connect
    timeout.reset();
    timeout.start();
    while (timeout.read_ms() < SMTP_TIMEOUT) {
        if (tcp_ready) break;
        Net::poll();
    }
    timeout.stop();
    if (! tcp_ready) goto exit;
    if (wait_smtp(220)) goto exit;

    // send request
    wait_ms(100);
    smtp->send("EHLO mbed\r\n", 11);
    if (wait_smtp(250)) goto exit;

    if (user && pwd) {
        // smtp auth
        char tmp[80], buf[100];
        int len;
        snprintf(tmp, sizeof(tmp), "%s%c%s%c%s", user, 0, user, 0, pwd);
        len = strlen(user) * 2 + strlen(pwd) + 2;
        base64enc(tmp, len, buf, sizeof(buf));
        smtp->send("AUTH PLAIN ", 11);
        smtp->send(buf, strlen(buf));
        smtp->send("\r\n", 2);
        if (wait_smtp(235)) goto quit;
    }

    smtp->send("MAIL FROM: ", 11);
    smtp->send(from, strlen(from));
    smtp->send("\r\n", 2);
    if (wait_smtp(250)) goto quit;

    smtp->send("RCPT TO: ", 9);
    smtp->send(to, strlen(to));
    smtp->send("\r\n", 2);
    if (wait_smtp(250)) goto quit;

    smtp->send("DATA\r\n", 6);
    if (wait_smtp(354)) goto quit;

    // mail data
    smtp->send(data, strlen(data));
    smtp->send("\r\n.\r\n", 5);
    if (wait_smtp(250)) goto quit;
    ret = 0;

quit:
    smtp->send("QUIT\r\n", 6);
    if (wait_smtp(221)) goto exit;

exit:
    smtp->resetOnEvent();
    smtp->close();
    delete smtp;

    return ret;
}