SMTP.cc

Go to the documentation of this file.
00001 /*
00002  *    Copyright 2005-2006 Intel Corporation
00003  * 
00004  *    Licensed under the Apache License, Version 2.0 (the "License");
00005  *    you may not use this file except in compliance with the License.
00006  *    You may obtain a copy of the License at
00007  * 
00008  *        http://www.apache.org/licenses/LICENSE-2.0
00009  * 
00010  *    Unless required by applicable law or agreed to in writing, software
00011  *    distributed under the License is distributed on an "AS IS" BASIS,
00012  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  *    See the License for the specific language governing permissions and
00014  *    limitations under the License.
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         // handle the initial message
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("."); // escape
00095         }
00096         if (end != start) { // handle blank lines
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';    // null terminate the input line
00187 
00188     memcpy(cmd, line, 4);
00189     cmd[4] = '\0';
00190 
00191     if (false) {} // symmetry
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         // send waiting for mail message
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             // check for escaped . or end of message (. on a line by itself)
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 } // namespace oasys

Generated on Sat Sep 8 08:36:18 2007 for DTN Reference Implementation by  doxygen 1.5.3