#include "MailFileSystem.h" # include "INotify.h" # include "Log.h" # include "File.h" # include namespace mail { MailFileSystem::MailFileSystem(coreutils::ZString &mailPath) : mailPath(mailPath) { queuePath << mailPath << "/.queue/"; sendLogPath << mailPath << "/.sendlog/"; // Harmless if it already exists (EEXIST is ignored, same as // every other error here - a failed mkdir just means the later // File open in SendMail::deliver() throws instead, which does // not affect whether the message itself is delivered). mkdir(sendLogPath.c_str(), 0770); } bool MailFileSystem::ifMailBoxExists(coreutils::ZString &mailbox) { coreutils::Log(coreutils::LOG_DEBUG_2) << "if exists mailbox: " << mailbox << "."; if(stat(getMailBoxPath(mailbox).c_str(), &statbuf) != -1) if(S_ISDIR(statbuf.st_mode)) return true; coreutils::Log(coreutils::LOG_DEBUG_2) << "doesnt exist mailbox: " << mailbox << "."; return false; } bool MailFileSystem::ifAliasExists(coreutils::ZString &alias) { if(stat(getMailBoxPath(alias).c_str(), &statbuf) != -1) if(S_ISREG(statbuf.st_mode)) return true; return false; } coreutils::MString MailFileSystem::getMailBoxPath(coreutils::ZString &mailbox) { mailbox.split("@", 1); // A mailbox string with no "@" splits to a single-element list - // mailbox[1] would then be an out-of-bounds std::vector access // (ZString::operator[] doesn't bounds-check), not even a // catchable exception. Previously unreachable with a safe input // in practice; now reachable directly from a client-supplied IMAP // LOGIN/POP3 USER username, so a malformed one (a bare "barant" // instead of "barant@barant.com", an easy real typo) needs to // fail cleanly here rather than read adjacent memory. if (mailbox.getList().size() < 2) return coreutils::MString(); coreutils::MString path; path << mailPath << "/" << mailbox[1] << "/" << mailbox[0]; return path; } std::vector MailFileSystem::getAliasMailboxes(coreutils::ZString &alias) { std::vector returnValue; return returnValue; } coreutils::ZString& MailFileSystem::getMailPath() { return mailPath; } coreutils::ZString& MailFileSystem::getQueuePath() { return queuePath; } coreutils::ZString& MailFileSystem::getSendLogPath() { return sendLogPath; } bool MailFileSystem::checkPassword(coreutils::ZString &userName, coreutils::ZString &password) { // File's constructor throws if the mailbox or its .password file // doesn't exist - an unknown username needs to come back as "not // authenticated", not an uncaught exception. Previously (as // __SMTP_AUTH::authLogin(), before this logic moved here) this // was only saved from crashing the whole process by // CommandList::processRequest()'s own unrelated catch-all, which // treated the exception as "could not be parsed" and silently // dropped the reply entirely - a client attempting AUTH LOGIN (or // now IMAP LOGIN, POP3 USER/PASS) against a nonexistent user got // no response at all rather than a clean authentication failure. try { coreutils::MString secretPath; secretPath << getMailBoxPath(userName) << "/.password"; coreutils::File secret(secretPath); secret.read(); return password == secret.asZString(); } catch (...) { return false; } } coreutils::MString MailFileSystem::getFolderPath(coreutils::ZString &mailbox, std::string folder) { coreutils::MString path; path << getMailBoxPath(mailbox) << "/" << folder; return path; } bool MailFileSystem::ifFolderExists(coreutils::ZString &mailbox, std::string folder) { struct stat folderStat; return stat(getFolderPath(mailbox, folder).c_str(), &folderStat) != -1 && S_ISDIR(folderStat.st_mode); } std::vector MailFileSystem::listFolders(coreutils::ZString &mailbox) { std::vector folders; std::string root = getMailBoxPath(mailbox).str(); if (!std::filesystem::is_directory(root)) return folders; for (const auto &entry : std::filesystem::directory_iterator(root)) { std::string name = entry.path().filename().string(); if (!name.empty() && name[0] != '.' && entry.is_directory()) folders.push_back(coreutils::MString(name)); } return folders; } }