Files
ServerCore/CommandList.cpp
T
Brad Arant 1a15f6ced8 Add real authentication to ConsoleServer/ConsoleSession, fix CommandList help
ConsoleSession's login flow never actually checked anything: the
entered username was discarded and the password was never compared,
so any input (including blank) reached the command prompt. Confirmed
live via an unauthenticated login against BARANTMail's admin console
on port 1027, which was reachable externally. ConsoleServer now takes
an optional credentialChecker callback consulted by the WAIT_PASSWORD
state; omitting it preserves the old accept-anything behavior for any
other use of this class.

Separately, the console never prompted until the client's first line
arrived - that line was silently discarded just to kick the state
machine off, shifting every subsequent line one slot early. Harmless
under the old accept-anything login, but it broke real credential
checking outright (the typed password was compared against the typed
username, etc). ConsoleSession::onConnected() now starts the state
machine immediately on connect instead.

CommandList::processCommand() was a no-op stub - the intended
"typing help lists the registered commands" body was commented out,
and even referenced a Command::getName() that was never added to the
base class. The map's own key from add() already is the registered
name, so this just walks it directly.
2026-09-06 04:05:59 +00:00

72 lines
2.6 KiB
C++

#include "CommandList.h"
#include "Log.h"
#include <algorithm>
#include <cctype>
namespace core {
CommandList::CommandList(std::string delimiter, int depth) : delimiter(delimiter), depth(depth) {}
void CommandList::add(Command &command, std::string name) {
commands.insert(std::make_pair(name, &command));
}
void CommandList::remove(Command &command) {}
int CommandList::processRequest(coreutils::ZString &request, TCPSession &session) {
if(session.grab != NULL) {
return session.grab->processCommand(request, session);
}
else {
if(request.equals(""))
return 0;
request.split(delimiter, depth);
request.reset();
try {
// Every protocol built on this (SMTP, POP3, IMAP - RFC 5321
// 2.4, RFC 1939 3, RFC 3501 2.2.1) specifies that command
// keywords are case-insensitive, but commands are always
// registered here in uppercase and this was an exact-match
// lookup - "mail from:" or "Mail From:" (both legal on the
// wire, and not unusual from real clients) silently failed
// to dispatch at all. Only the lookup key is case-folded;
// the token instances handlers actually receive are
// untouched, so any handler doing its own case-sensitive
// parsing on arguments is unaffected.
std::string key = request[0].str();
std::transform(key.begin(), key.end(), key.begin(),
[](unsigned char c) { return std::toupper(c); });
auto command = commands.at(key);
return command->processCommand(request, session);
}
catch(...) {
return 0;
}
}
return true;
}
bool CommandList::grabInput(TCPSession &session, Command &command) {
session.grab = &command;
return true;
}
void CommandList::clearGrab(TCPSession &session) {
session.grab = NULL;
}
int CommandList::processCommand(coreutils::ZString &request, TCPSession &session) {
// The intent was always "typing help lists the available
// commands" - this was left as a no-op stub (the commented-out
// body even called a Command::getName() that was never added to
// the base class) so every registered CommandList - including
// the top-level one behind the "help" command itself - silently
// did nothing. The map's own key IS the registered name (see
// add()), so no getName() is needed at all - just walk it directly.
for (auto &pair : commands)
session.out << pair.first << std::string("\r\n");
return true;
}
}