#include "__imapaccounts.h" #include "Base64.h" #include "Exception.h" #include namespace jet { std::string __imapaccounts::jsonEscape(const std::string &s) { std::string out; out.reserve(s.size() + 8); for (unsigned char c : s) { switch (c) { case '"': out += "\\\""; break; case '\\': out += "\\\\"; break; case '\n': out += "\\n"; break; case '\r': continue; case '\t': out += "\\t"; break; default: if (c < 0x20) { char buf[8]; snprintf(buf, sizeof(buf), "\\u%04x", c); out += buf; } else { out += (char)c; } } } return out; } static std::string trimWhitespace(const std::string &s) { size_t start = s.find_first_not_of(" \t\r\n"); if (start == std::string::npos) return std::string(); size_t end = s.find_last_not_of(" \t\r\n"); return s.substr(start, end - start + 1); } std::vector> __imapaccounts::decodeList(const std::string &encodedRaw) { std::vector> entries; coreutils::Base64 base64; // The list value round-trips through / template // captures, which include whatever whitespace/newlines surround // the tag call in the .jet source - trimming here (rather than // insisting every template call site stays whitespace-free) is // the robust fix, since a base64 decoder that stops at the first // invalid character would otherwise silently truncate to nothing // when a stray leading newline precedes real, valid content. std::string encoded = trimWhitespace(encodedRaw); size_t pos = 0; while (pos < encoded.size()) { size_t semi = encoded.find(';', pos); std::string entry = (semi == std::string::npos) ? encoded.substr(pos) : encoded.substr(pos, semi - pos); pos = (semi == std::string::npos) ? encoded.size() : semi + 1; entry = trimWhitespace(entry); if (entry.empty()) continue; size_t colon = entry.find(':'); if (colon == std::string::npos) continue; std::string userB64 = trimWhitespace(entry.substr(0, colon)); std::string passB64 = trimWhitespace(entry.substr(colon + 1)); std::string user = base64.decode(userB64).str(); std::string password = base64.decode(passB64).str(); entries.push_back(std::make_pair(user, password)); } return entries; } std::string __imapaccounts::encodeList(const std::vector> &entries) { coreutils::Base64 base64; std::string result; for (size_t i = 0; i < entries.size(); i++) { if (i > 0) result += ";"; std::string user = entries[i].first; std::string password = entries[i].second; result += base64.encode(user).str(); result += ":"; result += base64.encode(password).str(); } return result; } __imapaccounts::__imapaccounts(coreutils::ZString &in, coreutils::MString &parentOut, Global &global, Tag *parent, Tag *local) : Tag(in, parentOut, global, parent, local) { std::string action = trimWhitespace(resolveKeyword("action").str()); std::string listStr = resolveKeyword("list").str(); auto entries = decodeList(listStr); if (action == "add") { std::string user = trimWhitespace(resolveKeyword("user").str()); std::string password = trimWhitespace(resolveKeyword("password").str()); for (size_t i = 0; i < entries.size(); i++) { if (entries[i].first == user) { entries.erase(entries.begin() + (long)i); break; } } entries.push_back(std::make_pair(user, password)); out = coreutils::MString(encodeList(entries)); } else if (action == "remove") { std::string user = trimWhitespace(resolveKeyword("user").str()); for (size_t i = 0; i < entries.size(); i++) { if (entries[i].first == user) { entries.erase(entries.begin() + (long)i); break; } } out = coreutils::MString(encodeList(entries)); } else if (action == "getpassword") { std::string user = trimWhitespace(resolveKeyword("user").str()); std::string password; for (size_t i = 0; i < entries.size(); i++) { if (entries[i].first == user) { password = entries[i].second; break; } } out = coreutils::MString(password); } else if (action == "firstuser") { out = coreutils::MString(entries.empty() ? std::string() : entries[0].first); } else if (action == "list") { std::string active = resolveKeyword("active").str(); std::string json = "{\"accounts\":["; for (size_t i = 0; i < entries.size(); i++) { if (i > 0) json += ","; json += "\"" + jsonEscape(entries[i].first) + "\""; } json += "],\"active\":\"" + jsonEscape(active) + "\"}"; out = coreutils::MString(json); } else { throw coreutils::Exception("imapaccounts: unknown action \"" + action + "\"", __FILE__, __LINE__, 500); } } }