/** * @file imapserver.cc * @brief Implementation of the IMAPServer class. * @author Andreas Aardal Hanssen * @date 2005 */ #include "imapserver.h" #include "broker.h" #include "globals.h" #include "imapparser.h" #include "iodevice.h" #include "iofactory.h" #include "operators.h" #include "session.h" #include using namespace Binc; using std::endl; namespace Binc { void showGreeting(); }; IMAPServer::IMAPServer(int argc, char **argv) { this->argc = argc; this->argv = argv; this->stubMode = false; Session::getInstance().setState(Session::AUTHENTICATED); } IMAPServer::~IMAPServer() {} int IMAPServer::initialize() { Session &session = Session::getInstance(); if (!session.initialize(argc, argv)) return 111; return 0; } void IMAPServer::prepareForNextRequest() { serverStatus = ServerStatus::OK; bincClient.setFlags(IODevice::HasInputLimit); bincClient.flush(); bincClient.setMaxInputBufferSize(INPUT_BUFFER_LIMIT); Session::getInstance().setLastError(""); Session::getInstance().clearResponseCode(); } int IMAPServer::runStub() { bincDebug << "IMAPServer::runStub(), running stub" << endl; this->stubMode = true; Session::getInstance().setState(Session::NONAUTHENTICATED); return run(); } int IMAPServer::run() { Session &session = Session::getInstance(); bincLog.setOutputLevelLimit(IODevice::LogLevel::InfoLevel); std::string pid = std::to_string(session.getPid()); bincDebug << "IMAPServer::run(), started server" << endl; if (this->stubMode) { if (session.hasEnv("PROTOCOLDUMP")) bincClient.enableProtocolDumping(); bincLog << "bincimap-up: pid " << pid << " Connected: <" << session.getIP() << ">\n"; showGreeting(); } else { bincLog << "bincimapd: pid " << pid << " Logged in: <" << session.getEnv("USER") << "@" << session.getEnv("TCPREMOTEIP") << ">\n"; } bincLog.flush(); do { bincDebug << "IMAPServer::run(), preparing for next request" << endl; prepareForNextRequest(); // Find the current state's broker. BrokerFactory &brokerFactory = BrokerFactory::getInstance(); Broker *broker = brokerFactory.getBroker(session.getState()); bincDebug << "IMAPServer::run(), found broker " << (uintptr_t)broker << " for state " << session.getState() << endl; bool skipToNextRequest = false; // Parse the stub of the IMAP request. Request clientRequest; Parser::ParseResult stubParseResult = broker->parseStub(clientRequest); if (stubParseResult == Parser::ParseResult::TIMEOUT) { serverStatus = ServerStatus::Timeout; break; } else if (stubParseResult == Parser::ParseResult::REJECT) { serverStatus = ServerStatus::RequestRejected; } else if (stubParseResult == Parser::ParseResult::ERROR) { serverStatus = ServerStatus::RequestError; } else { // Find an operator that recognizes the name of the request, and // have it continue the parsing. Operator *o = broker->get(clientRequest.getName()); if (!o) { serverStatus = ServerStatus::RequestRejected; std::string err = "The command \""; if (clientRequest.getUidMode()) err += "UID "; err += clientRequest.getName(); err += "\" is unsupported in this state. "; session.setLastError(err); skipToNextRequest = true; } else { Parser::ParseResult parseResult = o->parse(clientRequest, broker->parser); if (parseResult == Parser::ParseResult::TIMEOUT) { serverStatus = ServerStatus::Timeout; } else if (parseResult == Parser::ParseResult::REJECT) { serverStatus = ServerStatus::RequestRejected; } else if (parseResult == Parser::ParseResult::ERROR) { serverStatus = ServerStatus::RequestError; } else { session.addStatement(); Depot *dep = session.getDepot(); switch (o->process(*dep, clientRequest)) { case Operator::ProcessResult::OK: break; case Operator::ProcessResult::NO: serverStatus = ServerStatus::RequestRejected; break; case Operator::ProcessResult::BAD: serverStatus = ServerStatus::RequestError; break; case Operator::ProcessResult::NOTHING: serverStatus = ServerStatus::RequestIgnore; // answer given already break; case Operator::ProcessResult::ABORT: session.setState(Session::LOGOUT); } } } } // If a syntax error was detected, we skip all characters in the // input stream up to and including '\n'. if (serverStatus == ServerStatus::RequestRejected) { bincClient << clientRequest.getTag() << " NO " << session.getResponseCode() << clientRequest.getName() << " failed: " << session.getLastError() << endl; } else if (serverStatus == ServerStatus::RequestError) { bincClient << "* BAD " << session.getLastError() << endl; skipToNextRequest = true; } else if (serverStatus == ServerStatus::RequestIgnore) { ; } else if (serverStatus == ServerStatus::OK && session.getState() != Session::LOGOUT) { bincClient << clientRequest.getTag() << " OK"; if (clientRequest.getUidMode()) bincClient << " UID"; bincClient << " " << session.getResponseCode() << clientRequest.getName() << " completed"; if (clientRequest.getContextInfo() != "") bincClient << " (" << clientRequest.getContextInfo() << ")"; bincClient << endl; } else { // Timeout, ClientDisconnected session.setState(Session::LOGOUT); } bincClient.flush(); if (skipToNextRequest) { if (!bincClient.skipTo('\n')) { if (bincClient.getLastError() == IODevice::Error::Timeout) serverStatus = ServerStatus::Timeout; else serverStatus = ServerStatus::ClientDisconnected; break; } } } while (session.getState() != Session::LOGOUT); // do line 81 // Session finished - write some log information std::string userID = this->stubMode ? session.getIP() : session.getEnv("USER"); if (this->stubMode) { bincLog << "bincimap-up: pid " << pid << " (Read: " << session.getReadBytes() << " Written: " << session.getWriteBytes() << ")\n"; } else { bincLog << "bincimapd: pid " << pid << " (Bodies: " << session.getBodies() << " Statements: " << session.getStatements() << ")\n"; } if (serverStatus == ServerStatus::Timeout) { bincClient << "* BYE Timeout after " << session.timeout() << " seconds of inactivity\n"; bincClient.flush(); bincLog << "bincimapd: pid " << pid << " Timed out: <" << userID << "> after " << IDLE_TIMEOUT << "s"; } else if (serverStatus == ServerStatus::ClientDisconnected) { bincLog << "bincimapd: pid " << pid << "Disconnected: <" << userID << ">\n"; } else { if (this->stubMode) { bincLog << "bincimap-up: pid " << pid << " Logged out: <" << userID << ">\n"; } else { bincLog << "bincimapd: pid " << pid << " Disconnected: <" << userID << ">\n"; } } bincLog.flush(); return 0; }