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