SMTP.cc

Go to the documentation of this file.
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         // handle the initial message
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("."); // escape
00079         }
00080         if (end != start) { // handle blank lines
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';    // null terminate the input line
00171 
00172     memcpy(cmd, line, 4);
00173     cmd[4] = '\0';
00174 
00175     if (false) {} // symmetry
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         // send waiting for mail message
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             // check for escaped . or end of message (. on a line by itself)
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 } // namespace oasys

Generated on Fri Dec 22 14:48:00 2006 for DTN Reference Implementation by  doxygen 1.5.1