/* * Baltisot * Copyright (C) 1999-2008 Nicolas "Pixel" Noble * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include const timeval timeout = { 8, 0 }; const timeval timeout_sending = { 80, 0 }; class ProcessSMTPRequest : public Task { public: ProcessSMTPRequest(const Socket & out) : s(out) { SetBurst(); } virtual ~ProcessSMTPRequest() { } virtual String GetName() { return "Processing SMTP request"; } protected: virtual int Do() throw (GeneralException); private: Socket s; Buffer b; Task * c; String from; std::vector tos; }; static Regex single_dot("^\\.$"), dotted_line("^\\..+$"); int ProcessSMTPRequest::Do() throw (GeneralException) { String l, cmd; Buffer * out; if (TaskMan::Event() == Task::EVT_TIMEOUT) { Suspend(TASK_DONE); } RemoveTimeout(); switch (current) { case 0: if (!s.IsConnected()) return TASK_DONE; b << "220 foo.bar Service Ready\r\n"; c = new CopyJob(&b, &s); WaitFor(c); current = 1; WaitFor(timeout); Suspend(TASK_ON_HOLD); case 1: delete c; b.reset(); c = new ReadJob(&s, &b, any); WaitFor(c); current = 2; WaitFor(timeout); Suspend(TASK_ON_HOLD); case 2: delete c; current = 1; b >> l; l.toupper(); cmd = l.extract(0, 3); if (cmd == "HELO") { b << "250 World.\r\n"; } else if (cmd == "MAIL") { if (l.extract(0, 9) != "MAIL FROM:") { b << "501 Syntax error.\r\n"; } else { b << "250 Ok.\r\n"; from = l.extract(10).trim(); } } else if (cmd == "RCPT") { if (l.extract(0, 7) != "RCPT TO:") { b << "501 Syntax error.\r\n"; } else { b << "250 Ok.\r\n"; tos.push_back(l.extract(8).trim()); } } else if (cmd == "DATA") { current = 4; b << "354 Ok, go ahead.\r\n"; } else if (cmd == "RSET") { from = ""; while (!tos.empty()) tos.pop_back(); b << "250 Ok.\r\n"; } else if (cmd == "NOOP") { b << "250 Zzz.\r\n"; } else if (cmd == "QUIT") { b << "221 Bye.\r\n"; current = 3; } else { b << "502 Unknow command.\r\n"; } c = new CopyJob(&b, &s); WaitFor(c); WaitFor(timeout); Suspend(TASK_ON_HOLD); case 3: delete c; Suspend(TASK_DONE); case 4: delete c; b.reset(); c = new ReadJob(&s, &b, single_dot); WaitFor(c); current = 5; WaitFor(timeout_sending); Suspend(TASK_ON_HOLD); case 5: delete c; out = new Buffer(); for (;;) { b >> l; if (single_dot.Match(l)) break; if (dotted_line.Match(l)) { (*out) << l.extract(1); } else { (*out) << l; } } c = MailHandler::ProcessMail(out, from, tos); if (c) { current = 6; WaitFor(c); Suspend(TASK_ON_HOLD); } case 6: if (c) delete c; delete out; from = ""; while (!tos.empty()) tos.pop_back(); b << "250 Ok.\r\n"; c = new CopyJob(&b, &s); WaitFor(c); current = 1; WaitFor(timeout); Suspend(TASK_ON_HOLD); } } MailHandler * MailHandler::head = 0; iLock MailHandler::lock; MailHandler::MailHandler() { lock.lock(); prev = 0; next = head; head = this; if (next) next->prev = this; lock.unlock(); } MailHandler::~MailHandler() { lock.lock(); if (prev) prev->next = next; if (next) next->prev = prev; if (head == this) head = next; lock.unlock(); } Task * MailHandler::ProcessMail(Handle * in, const String & from, std::vector tos) { MailHandler * cur; Task * r; lock.lock(); for (cur = head; cur; cur = cur->next) { if (( r = cur->doProcessMail(in, from, tos))) { lock.unlock(); return r; } } lock.unlock(); return 0; } void MailHandler::OnTop() { if (head == this) return; lock.lock(); if (prev) prev->next = next; if (next) next->prev = prev; prev = 0; next = head; head = this; if (next) next->prev = this; lock.unlock(); } MailServer::MailServer(int _port, const String & _name) throw (GeneralException) : name(_name), localport(_port) { bool r; r = Listener.SetLocal("", localport); if (!r) { throw GeneralException("Initialisation of the Mini SMTP-Server failed: can't bind"); } r = Listener.Listen(); if (!r) { throw GeneralException("Initialisation of the Mini SMTP-Server failed: can't listen"); } Listener.SetNonBlock(); WaitFor(&Listener, W4_STICKY | W4_READING); } MailServer::~MailServer(void) { Listener.close(); } int MailServer::Do() throw (GeneralException) { try { Socket s = Listener.Accept(); s.SetNonBlock(); new ProcessSMTPRequest(s); } catch (GeneralException) { } return TASK_ON_HOLD; } String MailServer::GetName() { return String("Mini SMTP-Server '") + name + "'"; }