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