Tiny SMTP Client
Dependencies: EthernetNetIf mbed
TinySMTP.cpp@1:05064fe7ea1e, 2011-07-28 (annotated)
- Committer:
- okini3939
- Date:
- Thu Jul 28 17:39:31 2011 +0000
- Revision:
- 1:05064fe7ea1e
- Parent:
- 0:7440c9b170aa
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
okini3939 | 0:7440c9b170aa | 1 | /* |
okini3939 | 0:7440c9b170aa | 2 | * mbed Tiny SMTP Client |
okini3939 | 0:7440c9b170aa | 3 | * Copyright (c) 2011 Hiroshi Suga |
okini3939 | 0:7440c9b170aa | 4 | * Released under the MIT License: http://mbed.org/license/mit |
okini3939 | 0:7440c9b170aa | 5 | */ |
okini3939 | 0:7440c9b170aa | 6 | |
okini3939 | 0:7440c9b170aa | 7 | /** @file |
okini3939 | 0:7440c9b170aa | 8 | * @brief Tiny SMTP Client |
okini3939 | 0:7440c9b170aa | 9 | */ |
okini3939 | 0:7440c9b170aa | 10 | |
okini3939 | 0:7440c9b170aa | 11 | #include "mbed.h" |
okini3939 | 0:7440c9b170aa | 12 | #include "EthernetNetIf.h" |
okini3939 | 0:7440c9b170aa | 13 | #include "TCPSocket.h" |
okini3939 | 0:7440c9b170aa | 14 | #include "DNSRequest.h" |
okini3939 | 0:7440c9b170aa | 15 | #include "TinySMTP.h" |
okini3939 | 0:7440c9b170aa | 16 | |
okini3939 | 0:7440c9b170aa | 17 | #define STATUS_NONE 0 |
okini3939 | 0:7440c9b170aa | 18 | #define STATUS_READABLE 1 |
okini3939 | 0:7440c9b170aa | 19 | #define STATUS_CONNECTED 2 |
okini3939 | 0:7440c9b170aa | 20 | #define STATUS_ERROR 3 |
okini3939 | 0:7440c9b170aa | 21 | #define STATUS_DISCONNECTED 4 |
okini3939 | 0:7440c9b170aa | 22 | |
okini3939 | 1:05064fe7ea1e | 23 | static TCPSocket *smtp; |
okini3939 | 1:05064fe7ea1e | 24 | static volatile int tcp_ready, tcp_readable, tcp_writable; |
okini3939 | 1:05064fe7ea1e | 25 | static volatile int dns_status; |
okini3939 | 0:7440c9b170aa | 26 | |
okini3939 | 0:7440c9b170aa | 27 | |
okini3939 | 0:7440c9b170aa | 28 | // Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com) |
okini3939 | 0:7440c9b170aa | 29 | int base64enc(const char *input, unsigned int length, char *output, int outputlen) { |
okini3939 | 0:7440c9b170aa | 30 | static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
okini3939 | 0:7440c9b170aa | 31 | unsigned int c, c1, c2, c3; |
okini3939 | 0:7440c9b170aa | 32 | |
okini3939 | 0:7440c9b170aa | 33 | if (outputlen < (((length-1)/3)+1)<<2) return -1; |
okini3939 | 0:7440c9b170aa | 34 | |
okini3939 | 0:7440c9b170aa | 35 | for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) { |
okini3939 | 0:7440c9b170aa | 36 | c1 = ((((unsigned char)*((unsigned char *)&input[i])))); |
okini3939 | 0:7440c9b170aa | 37 | c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0; |
okini3939 | 0:7440c9b170aa | 38 | c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0; |
okini3939 | 0:7440c9b170aa | 39 | |
okini3939 | 0:7440c9b170aa | 40 | c = ((c1 & 0xFC) >> 2); |
okini3939 | 0:7440c9b170aa | 41 | output[j+0] = base64[c]; |
okini3939 | 0:7440c9b170aa | 42 | c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4); |
okini3939 | 0:7440c9b170aa | 43 | output[j+1] = base64[c]; |
okini3939 | 0:7440c9b170aa | 44 | c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6); |
okini3939 | 0:7440c9b170aa | 45 | output[j+2] = (length>i+1)?base64[c]:'='; |
okini3939 | 0:7440c9b170aa | 46 | c = (c3 & 0x3F); |
okini3939 | 0:7440c9b170aa | 47 | output[j+3] = (length>i+2)?base64[c]:'='; |
okini3939 | 0:7440c9b170aa | 48 | } |
okini3939 | 0:7440c9b170aa | 49 | output[(((length-1)/3)+1)<<2] = '\0'; |
okini3939 | 0:7440c9b170aa | 50 | return 0; |
okini3939 | 0:7440c9b170aa | 51 | } |
okini3939 | 0:7440c9b170aa | 52 | |
okini3939 | 0:7440c9b170aa | 53 | |
okini3939 | 0:7440c9b170aa | 54 | void isr_smtp (TCPSocketEvent e) { |
okini3939 | 0:7440c9b170aa | 55 | |
okini3939 | 0:7440c9b170aa | 56 | #ifdef DEBUG |
okini3939 | 0:7440c9b170aa | 57 | printf("tcp(%d)\r\n", e); |
okini3939 | 0:7440c9b170aa | 58 | #endif |
okini3939 | 0:7440c9b170aa | 59 | switch(e) { |
okini3939 | 1:05064fe7ea1e | 60 | case TCPSOCKET_CONNECTED: |
okini3939 | 1:05064fe7ea1e | 61 | tcp_ready = 1; |
okini3939 | 0:7440c9b170aa | 62 | break; |
okini3939 | 0:7440c9b170aa | 63 | |
okini3939 | 1:05064fe7ea1e | 64 | case TCPSOCKET_READABLE: //Incoming data |
okini3939 | 1:05064fe7ea1e | 65 | tcp_readable = 1; |
okini3939 | 1:05064fe7ea1e | 66 | break; |
okini3939 | 1:05064fe7ea1e | 67 | |
okini3939 | 0:7440c9b170aa | 68 | case TCPSOCKET_WRITEABLE: //We can send data |
okini3939 | 1:05064fe7ea1e | 69 | tcp_writable = 1; |
okini3939 | 0:7440c9b170aa | 70 | break; |
okini3939 | 0:7440c9b170aa | 71 | |
okini3939 | 0:7440c9b170aa | 72 | case TCPSOCKET_CONTIMEOUT: |
okini3939 | 0:7440c9b170aa | 73 | case TCPSOCKET_CONRST: |
okini3939 | 0:7440c9b170aa | 74 | case TCPSOCKET_CONABRT: |
okini3939 | 0:7440c9b170aa | 75 | case TCPSOCKET_ERROR: |
okini3939 | 0:7440c9b170aa | 76 | case TCPSOCKET_DISCONNECTED: |
okini3939 | 1:05064fe7ea1e | 77 | tcp_ready = 0; |
okini3939 | 0:7440c9b170aa | 78 | break; |
okini3939 | 0:7440c9b170aa | 79 | } |
okini3939 | 0:7440c9b170aa | 80 | } |
okini3939 | 0:7440c9b170aa | 81 | |
okini3939 | 0:7440c9b170aa | 82 | int wait_smtp (int code) { |
okini3939 | 1:05064fe7ea1e | 83 | Timer timeout; |
okini3939 | 0:7440c9b170aa | 84 | int i; |
okini3939 | 1:05064fe7ea1e | 85 | char buf[1500]; |
okini3939 | 0:7440c9b170aa | 86 | |
okini3939 | 0:7440c9b170aa | 87 | // wait responce |
okini3939 | 1:05064fe7ea1e | 88 | timeout.reset(); |
okini3939 | 1:05064fe7ea1e | 89 | timeout.start(); |
okini3939 | 1:05064fe7ea1e | 90 | while (timeout.read_ms() < SMTP_TIMEOUT) { |
okini3939 | 1:05064fe7ea1e | 91 | if (tcp_readable) break; |
okini3939 | 0:7440c9b170aa | 92 | Net::poll(); |
okini3939 | 0:7440c9b170aa | 93 | } |
okini3939 | 1:05064fe7ea1e | 94 | timeout.stop(); |
okini3939 | 1:05064fe7ea1e | 95 | if (! tcp_readable) return -1; |
okini3939 | 0:7440c9b170aa | 96 | // recv |
okini3939 | 0:7440c9b170aa | 97 | i = smtp->recv(buf, sizeof(buf)); |
okini3939 | 1:05064fe7ea1e | 98 | if (i < sizeof(buf) - 1) tcp_readable = 0; |
okini3939 | 0:7440c9b170aa | 99 | buf[i] = 0; |
okini3939 | 0:7440c9b170aa | 100 | #ifdef DEBUG |
okini3939 | 0:7440c9b170aa | 101 | printf(buf); |
okini3939 | 0:7440c9b170aa | 102 | #endif |
okini3939 | 0:7440c9b170aa | 103 | |
okini3939 | 0:7440c9b170aa | 104 | // check return code |
okini3939 | 0:7440c9b170aa | 105 | if (atoi(buf) == code) return 0; |
okini3939 | 0:7440c9b170aa | 106 | |
okini3939 | 0:7440c9b170aa | 107 | return -1; |
okini3939 | 0:7440c9b170aa | 108 | } |
okini3939 | 0:7440c9b170aa | 109 | |
okini3939 | 0:7440c9b170aa | 110 | void isr_dns (DNSReply r) { |
okini3939 | 0:7440c9b170aa | 111 | |
okini3939 | 1:05064fe7ea1e | 112 | #ifdef DEBUG |
okini3939 | 1:05064fe7ea1e | 113 | printf("dns(%d)\r\n", r); |
okini3939 | 1:05064fe7ea1e | 114 | #endif |
okini3939 | 0:7440c9b170aa | 115 | if (DNS_FOUND) { |
okini3939 | 0:7440c9b170aa | 116 | dns_status = 1; |
okini3939 | 0:7440c9b170aa | 117 | } else { |
okini3939 | 0:7440c9b170aa | 118 | dns_status = -1; |
okini3939 | 0:7440c9b170aa | 119 | } |
okini3939 | 0:7440c9b170aa | 120 | } |
okini3939 | 0:7440c9b170aa | 121 | |
okini3939 | 0:7440c9b170aa | 122 | int sendmail (char *to, char *from, char *data, Host *host, char *user, char *pwd) { |
okini3939 | 0:7440c9b170aa | 123 | TCPSocketErr err; |
okini3939 | 1:05064fe7ea1e | 124 | Timer timeout; |
okini3939 | 1:05064fe7ea1e | 125 | int ret = -1; |
okini3939 | 0:7440c9b170aa | 126 | |
okini3939 | 0:7440c9b170aa | 127 | smtp = new TCPSocket; |
okini3939 | 1:05064fe7ea1e | 128 | tcp_ready = 0; |
okini3939 | 1:05064fe7ea1e | 129 | tcp_readable = 0; |
okini3939 | 1:05064fe7ea1e | 130 | tcp_writable = 0; |
okini3939 | 0:7440c9b170aa | 131 | |
okini3939 | 0:7440c9b170aa | 132 | smtp->setOnEvent(isr_smtp); |
okini3939 | 0:7440c9b170aa | 133 | |
okini3939 | 0:7440c9b170aa | 134 | // connect |
okini3939 | 0:7440c9b170aa | 135 | if (host->getIp().isNull()) { |
okini3939 | 0:7440c9b170aa | 136 | DNSRequest dns; |
okini3939 | 0:7440c9b170aa | 137 | dns_status = 0; |
okini3939 | 0:7440c9b170aa | 138 | dns.setOnReply(isr_dns); |
okini3939 | 0:7440c9b170aa | 139 | if (dns.resolve(host) != DNS_OK) goto exit; |
okini3939 | 1:05064fe7ea1e | 140 | timeout.reset(); |
okini3939 | 1:05064fe7ea1e | 141 | timeout.start(); |
okini3939 | 1:05064fe7ea1e | 142 | while (timeout.read_ms() < SMTP_TIMEOUT) { |
okini3939 | 0:7440c9b170aa | 143 | if (dns_status) break; |
okini3939 | 0:7440c9b170aa | 144 | Net::poll(); |
okini3939 | 0:7440c9b170aa | 145 | } |
okini3939 | 1:05064fe7ea1e | 146 | timeout.stop(); |
okini3939 | 1:05064fe7ea1e | 147 | if (dns_status <= 0) goto exit; |
okini3939 | 1:05064fe7ea1e | 148 | #ifdef DEBUG |
okini3939 | 1:05064fe7ea1e | 149 | 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]); |
okini3939 | 1:05064fe7ea1e | 150 | #endif |
okini3939 | 0:7440c9b170aa | 151 | } |
okini3939 | 1:05064fe7ea1e | 152 | if (! host->getPort()) { |
okini3939 | 1:05064fe7ea1e | 153 | host->setPort(SMTP_PORT); |
okini3939 | 1:05064fe7ea1e | 154 | } |
okini3939 | 1:05064fe7ea1e | 155 | |
okini3939 | 0:7440c9b170aa | 156 | err = smtp->connect(*host); |
okini3939 | 0:7440c9b170aa | 157 | if (err != TCPSOCKET_OK) goto exit; |
okini3939 | 0:7440c9b170aa | 158 | |
okini3939 | 0:7440c9b170aa | 159 | // wait connect |
okini3939 | 1:05064fe7ea1e | 160 | timeout.reset(); |
okini3939 | 1:05064fe7ea1e | 161 | timeout.start(); |
okini3939 | 1:05064fe7ea1e | 162 | while (timeout.read_ms() < SMTP_TIMEOUT) { |
okini3939 | 1:05064fe7ea1e | 163 | if (tcp_ready) break; |
okini3939 | 0:7440c9b170aa | 164 | Net::poll(); |
okini3939 | 0:7440c9b170aa | 165 | } |
okini3939 | 1:05064fe7ea1e | 166 | timeout.stop(); |
okini3939 | 1:05064fe7ea1e | 167 | if (! tcp_ready) goto exit; |
okini3939 | 0:7440c9b170aa | 168 | if (wait_smtp(220)) goto exit; |
okini3939 | 0:7440c9b170aa | 169 | |
okini3939 | 0:7440c9b170aa | 170 | // send request |
okini3939 | 0:7440c9b170aa | 171 | wait_ms(100); |
okini3939 | 0:7440c9b170aa | 172 | smtp->send("EHLO mbed\r\n", 11); |
okini3939 | 0:7440c9b170aa | 173 | if (wait_smtp(250)) goto exit; |
okini3939 | 0:7440c9b170aa | 174 | |
okini3939 | 0:7440c9b170aa | 175 | if (user && pwd) { |
okini3939 | 0:7440c9b170aa | 176 | // smtp auth |
okini3939 | 0:7440c9b170aa | 177 | char tmp[80], buf[100]; |
okini3939 | 0:7440c9b170aa | 178 | int len; |
okini3939 | 0:7440c9b170aa | 179 | snprintf(tmp, sizeof(tmp), "%s%c%s%c%s", user, 0, user, 0, pwd); |
okini3939 | 0:7440c9b170aa | 180 | len = strlen(user) * 2 + strlen(pwd) + 2; |
okini3939 | 0:7440c9b170aa | 181 | base64enc(tmp, len, buf, sizeof(buf)); |
okini3939 | 0:7440c9b170aa | 182 | smtp->send("AUTH PLAIN ", 11); |
okini3939 | 0:7440c9b170aa | 183 | smtp->send(buf, strlen(buf)); |
okini3939 | 0:7440c9b170aa | 184 | smtp->send("\r\n", 2); |
okini3939 | 0:7440c9b170aa | 185 | if (wait_smtp(235)) goto quit; |
okini3939 | 0:7440c9b170aa | 186 | } |
okini3939 | 0:7440c9b170aa | 187 | |
okini3939 | 0:7440c9b170aa | 188 | smtp->send("MAIL FROM: ", 11); |
okini3939 | 0:7440c9b170aa | 189 | smtp->send(from, strlen(from)); |
okini3939 | 0:7440c9b170aa | 190 | smtp->send("\r\n", 2); |
okini3939 | 0:7440c9b170aa | 191 | if (wait_smtp(250)) goto quit; |
okini3939 | 0:7440c9b170aa | 192 | |
okini3939 | 0:7440c9b170aa | 193 | smtp->send("RCPT TO: ", 9); |
okini3939 | 0:7440c9b170aa | 194 | smtp->send(to, strlen(to)); |
okini3939 | 0:7440c9b170aa | 195 | smtp->send("\r\n", 2); |
okini3939 | 0:7440c9b170aa | 196 | if (wait_smtp(250)) goto quit; |
okini3939 | 0:7440c9b170aa | 197 | |
okini3939 | 0:7440c9b170aa | 198 | smtp->send("DATA\r\n", 6); |
okini3939 | 0:7440c9b170aa | 199 | if (wait_smtp(354)) goto quit; |
okini3939 | 0:7440c9b170aa | 200 | |
okini3939 | 0:7440c9b170aa | 201 | // mail data |
okini3939 | 0:7440c9b170aa | 202 | smtp->send(data, strlen(data)); |
okini3939 | 0:7440c9b170aa | 203 | smtp->send("\r\n.\r\n", 5); |
okini3939 | 0:7440c9b170aa | 204 | if (wait_smtp(250)) goto quit; |
okini3939 | 0:7440c9b170aa | 205 | ret = 0; |
okini3939 | 0:7440c9b170aa | 206 | |
okini3939 | 0:7440c9b170aa | 207 | quit: |
okini3939 | 0:7440c9b170aa | 208 | smtp->send("QUIT\r\n", 6); |
okini3939 | 0:7440c9b170aa | 209 | if (wait_smtp(221)) goto exit; |
okini3939 | 0:7440c9b170aa | 210 | |
okini3939 | 0:7440c9b170aa | 211 | exit: |
okini3939 | 0:7440c9b170aa | 212 | smtp->resetOnEvent(); |
okini3939 | 0:7440c9b170aa | 213 | smtp->close(); |
okini3939 | 0:7440c9b170aa | 214 | delete smtp; |
okini3939 | 0:7440c9b170aa | 215 | |
okini3939 | 0:7440c9b170aa | 216 | return ret; |
okini3939 | 0:7440c9b170aa | 217 | } |