00001 #include <ctype.h>
00002
00003 #include "SMTP.h"
00004
00005 namespace oasys {
00006
00007 const char* SMTP::nl_ = "\r\n";
00008 SMTP::Config SMTP::DEFAULT_CONFIG;
00009
00010
00011 SMTP::SMTP(oasys::BufferedInput* in,
00012 oasys::BufferedOutput* out,
00013 const Config& config,
00014 const char* logpath)
00015 : Logger("SMTP", logpath),
00016 in_(in),
00017 out_(out),
00018 config_(config)
00019 {
00020 ASSERT(in_);
00021 ASSERT(out_);
00022
00023 in_->logpathf("%s/in", logpath);
00024 out_->logpathf("%s/out", logpath);
00025 }
00026
00027
00028 int
00029 SMTP::client_session(SMTPSender* sender, bool first_session)
00030 {
00031 int err;
00032
00033 std::string domain;
00034 std::string from;
00035 std::vector<std::string> to;
00036 std::string received;
00037 const std::string* message;
00038
00039 if (first_session) {
00040
00041 if ((err = process_response(220)) != 0) return err;
00042
00043 sender->get_HELO_domain(&domain);
00044 out_->printf("HELO %s\r\n", domain.c_str());
00045 if ((err = process_response(250)) != 0) return err;
00046 }
00047
00048 sender->get_MAIL_from(&from);
00049 out_->printf("MAIL FROM: %s\r\n", from.c_str());
00050 if ((err = process_response(250)) != 0) return err;
00051
00052 sender->get_RCPT_list(&to);
00053 for (size_t i = 0; i < to.size(); ++i) {
00054 out_->printf("RCPT TO: %s\r\n", to[i].c_str());
00055 if ((err = process_response(250)) != 0) return err;
00056 }
00057
00058 out_->printf("DATA\r\n");
00059 if ((err = process_response(354)) != 0) return err;
00060
00061 sender->get_RECEIVED(&received);
00062 sender->get_DATA(&message);
00063 size_t start = 0, end = 0;
00064
00065 if (received.length() != 0) {
00066 out_->write(received.data(), received.length());
00067 }
00068
00069 while (1) {
00070 end = message->find_first_of("\r\n", start);
00071
00072 if (end == std::string::npos) {
00073 end = message->length();
00074 }
00075
00076 const char* bp = message->data() + start;
00077 if (*bp == '.') {
00078 out_->write(".");
00079 }
00080 if (end != start) {
00081 out_->write(bp, end - start);
00082 }
00083 out_->write("\r\n");
00084
00085 if (end == message->length()) {
00086 break;
00087 }
00088
00089 start = end + 1;
00090 if ((*message)[start] == '\n') {
00091 ++start;
00092 }
00093
00094 if (start == message->length()) {
00095 break;
00096 }
00097 }
00098
00099 out_->write(".\r\n");
00100 out_->flush();
00101
00102 if ((err = process_response(250)) != 0) return err;
00103
00104 return 0;
00105 }
00106
00107
00108 int
00109 SMTP::server_session(oasys::SMTPHandler* handler)
00110 {
00111 int err = send_signon();
00112 if (err < 0) {
00113 log_warn("disconnecting: couldn't send sign on message");
00114 return err;
00115 }
00116
00117 while (true) {
00118 int resp = process_cmd(handler);
00119
00120 if (resp > 0) {
00121 err = send_response(resp);
00122 if (err < 0) {
00123 log_warn("disconnecting: couldn't send response");
00124 }
00125 if (resp == 221) {
00126 log_info("quit SMTP session");
00127 break;
00128 }
00129 } else if (resp == 0) {
00130 log_info("disconnecting: SMTP session on eof");
00131 break;
00132 } else {
00133 log_warn("disconnecting: SMTP session on unexpected error");
00134 break;
00135 }
00136 }
00137 return err;
00138 }
00139
00140
00141 int
00142 SMTP::send_signon()
00143 {
00144 return send_response(220);
00145 }
00146
00147
00148 int
00149 SMTP::process_cmd(SMTPHandler* handler)
00150 {
00151 char* line;
00152 int cc = in_->read_line(nl_, &line, config_.timeout_);
00153
00154 if (cc < 0) {
00155 log_warn("got error %d, disconnecting", cc);
00156 return -1;
00157 } else if (cc == 0) {
00158 log_info("got eof from connection");
00159 return 0;
00160 }
00161
00162 char cmd[5];
00163 log_debug("read cc=%d", cc);
00164 if (cc < 4) {
00165 log_info("garbage input command");
00166 return 500;
00167 }
00168
00169 ASSERT(line[cc - strlen(nl_)] == nl_[0]);
00170 line[cc - strlen(nl_)] = '\0';
00171
00172 memcpy(cmd, line, 4);
00173 cmd[4] = '\0';
00174
00175 if (false) {}
00176 else if (strcasecmp(cmd, "HELO") == 0)
00177 {
00178 if (line[4] != ' ') {
00179 return 501;
00180 }
00181
00182 #define SKIP_WS(_var) \
00183 while (1) { \
00184 if (*_var == '\0') { \
00185 return 501; \
00186 } \
00187 if (*_var != ' ') { \
00188 break; \
00189 } \
00190 ++_var; \
00191 }
00192
00193 char* domain = &line[5];
00194 SKIP_WS(domain);
00195 return handler->smtp_HELO(domain);
00196 }
00197 else if (strcasecmp(cmd, "MAIL") == 0)
00198 {
00199 if (strncasecmp(line, "MAIL FROM:", 10) != 0) {
00200 return 501;
00201 }
00202
00203 char* from = &line[10];
00204 SKIP_WS(from);
00205 return handler->smtp_MAIL(from);
00206 }
00207 else if (strcasecmp(cmd, "RCPT") == 0)
00208 {
00209 if (strncasecmp(line, "RCPT TO:", 8) != 0) {
00210 return 501;
00211 }
00212
00213 char* to = &line[8];
00214 SKIP_WS(to);
00215 return handler->smtp_RCPT(to);
00216 }
00217 else if (strcasecmp(cmd, "DATA") == 0)
00218 {
00219 int err = handler->smtp_DATA_begin();
00220 if (err != 0) {
00221 return err;
00222 }
00223
00224
00225 send_response(354);
00226
00227 while (true) {
00228 char* mail_line;
00229 int cc = in_->read_line(nl_, &mail_line, config_.timeout_);
00230 if (cc <= 0) {
00231 log_warn("got error %d, disconnecting", cc);
00232 return -1;
00233 }
00234
00235 ASSERT(cc >= static_cast<int>(strlen(nl_)));
00236 ASSERT(mail_line[cc - strlen(nl_)] == nl_[0]);
00237 mail_line[cc - strlen(nl_)] = '\0';
00238
00239
00240 if (mail_line[0] == '.') {
00241 if (strlen(mail_line) == 1) {
00242 break;
00243 }
00244 mail_line += 1;
00245 }
00246
00247 int err = handler->smtp_DATA_line(mail_line);
00248 if (err != 0) {
00249 return err;
00250 }
00251 }
00252
00253 return handler->smtp_DATA_end();
00254 }
00255 else if (strcasecmp(cmd, "RSET") == 0)
00256 {
00257 return handler->smtp_RSET();
00258 }
00259 else if (strcasecmp(cmd, "NOOP") == 0)
00260 {
00261 return 220;
00262 }
00263 else if (strcasecmp(cmd, "QUIT") == 0)
00264 {
00265 handler->smtp_QUIT();
00266 return 221;
00267 }
00268 else if (strcasecmp(cmd, "TURN") == 0 ||
00269 strcasecmp(cmd, "SEND") == 0 ||
00270 strcasecmp(cmd, "SOML") == 0 ||
00271 strcasecmp(cmd, "SAML") == 0 ||
00272 strcasecmp(cmd, "VRFY") == 0 ||
00273 strcasecmp(cmd, "EXPN") == 0 ||
00274 strcasecmp(cmd, "EHLO") == 0)
00275 {
00276 return 502;
00277 }
00278
00279 return 500;
00280 }
00281
00282
00283 int
00284 SMTP::process_response(int expected_code)
00285 {
00286 char* line;
00287 int cc = in_->read_line(nl_, &line, config_.timeout_);
00288
00289 if (cc < 0) {
00290 log_warn("got error %d, disconnecting", cc);
00291 return -1;
00292 } else if (cc == 0) {
00293 log_info("got eof from connection");
00294 return 221;
00295 }
00296
00297 log_debug("read cc=%d", cc);
00298
00299 if (cc < 3) {
00300 log_info("garbage response");
00301 return 500;
00302 }
00303
00304 char buf[4];
00305 memcpy(buf, line, 3);
00306 buf[3] = '\0';
00307
00308 char* end;
00309 int code = strtoul(buf, &end, 10);
00310 if (end != &buf[3]) {
00311 log_info("garbage code value %s", buf);
00312 return 501;
00313 }
00314
00315 if (code != expected_code) {
00316 log_info("code %d != expected %d", code, expected_code);
00317 return 503;
00318 }
00319
00320 log_debug("OK: %s", line);
00321
00322 return 0;
00323 }
00324
00325
00326 int
00327 SMTP::send_response(int code)
00328 {
00329 int err = out_->format_buf("%d ", code);
00330 if (err < 0) return err;
00331 return out_->printf(response_code(code), config_.domain_.c_str());
00332 }
00333
00334
00335 const char*
00336 SMTP::response_code(int code) const
00337 {
00338 switch (code) {
00339 case 211: return "System status, or system help reply\r\n";
00340 case 214: return "Help message\r\n";
00341 case 220: return "%s Service ready\r\n";
00342 case 221: return "%s Service closing transmission channel\r\n";
00343 case 250: return "Requested mail action okay, completed\r\n";
00344 case 251: return "User not local; will forward to nowhere.net\r\n";
00345 case 354: return "Start mail input; end with <CRLF>.<CRLF>\r\n";
00346 case 421: return "tierstore Service not available, "
00347 "closing transmission channel\r\n";
00348 case 450: return "Requested mail action not taken: mailbox unavailable\r\n";
00349 case 451: return "Requested action aborted: local error in processing\r\n";
00350 case 452: return "Requested action not taken: insufficient system storage\r\n";
00351 case 500: return "Syntax error, command unrecognized\r\n";
00352 case 501: return "Syntax error in parameters or arguments\r\n";
00353 case 502: return "Command not implemented\r\n";
00354 case 503: return "Bad sequence of commands\r\n";
00355 case 504: return "Command parameter not implemented\r\n";
00356 case 550: return "Requested action not taken: mailbox unavailable\r\n";
00357 case 551: return "User not local; please try nowhere.net\r\n";
00358 case 552: return "Requested mail action aborted: exceeded "
00359 "storage allocation\r\n";
00360 case 553: return "Requested action not taken: mailbox name not allowed\r\n";
00361 case 554: return "Transaction failed\r\n";
00362 default: return 0;
00363 }
00364 }
00365
00366 }