#include "config.hpp"
#include "imap.hpp"
#include "main.hpp"
#include <fstream>
static std::string str_pop(const char*& s) { // optionally quoted
while (*s == ' ' || *s == '\t') ++s;
if (*s == '"') {
s++;
const char* e = s;
while (*e != '"' && *e != '\0') ++e;
std::string rv(s, e-s);
s = (*e == '"')? e+1: e;
return rv;
} else {
const char* e = s;
while (*e != ' ' && *e != '\t' && *e != '\0') ++e;
std::string rv(s, e-s);
s = e;
return rv;
}
}
bool Config::parse_match(const char* str) { // match: match_id ...
std::string id = str_pop(str);
if (id.empty()) return false;
while (*str == ' ' || *str == '\t') ++str;
std::string match(str);
if (match.empty()) return false;
unsigned num = 0;
const char* mp = match.c_str();
if (!IMAP::check_match(mp, num)) {
LOG("cannot parse at match %u around pos %lu: '%s'", num+1, (uintptr_t)(mp-match.c_str()), match.c_str());
return false;
}
assert(num > 0);
if (num > 1) {
match = "(" + match + ")";
}
std::map<std::string, std::string>::iterator it = matches.find(id);
if (it != matches.end()) {
it->second = "OR " + it->second + " " + match;
} else {
matches.insert(std::pair<std::string, std::string>(id, match));
}
return true;
}
bool Config::parse_server(const char* str) { // server: server_id host port user pass
std::string id = str_pop(str);
if (id.empty() || servers.find(id) != servers.end() || matches.find(id) != matches.end()) {
return false;
}
server_t s;
s.host = str_pop(str);
s.port = str_pop(str);
s.user = str_pop(str);
s.pass = str_pop(str);
if (s.host.empty() || s.port.empty() || s.user.empty()) return false;
if (!IMAP::is_quotable(s.user.c_str()) || !IMAP::is_quotable(s.pass.c_str())) return false;
servers.insert(std::pair<std::string,server_t>(id, s));
return true;
}
bool Config::parse_task(const char* str) { // server_id src match_id dst
std::string id = str_pop(str);
if (id.empty() || servers.find(id) == servers.end()) {
return false;
}
agenda_t a;
a.from = str_pop(str);
a.match_id = str_pop(str);
a.to = str_pop(str);
if (a.match_id.empty() || matches.find(a.match_id) == matches.end()) return false;
if (a.from.empty() || !IMAP::is_quotable(a.from.c_str())) return false;
if (a.to.empty() || !IMAP::is_quotable(a.to.c_str())) return false;
servers.find(id)->second.tasks.push_back(a); // TODO: check for from/to duplicates? (could break ordering)
return true;
}
/*
server server-id1 host port user pass
match match-id1 ...
match match-id1 ...
match match-id2 ...
server-id1 Inbox match-id1 "Junk Mail"
*/
bool Config::parse_line(std::string& s) {
const char* c = s.c_str();
while (*c == ' ' || *c == '\t') c++;
if (!*c || *c == '#') return true;
if (strncasecmp(c, "server ", 7) == 0) return parse_server(c+7);
if (strncasecmp(c, "match ", 6) == 0) return parse_match(c+6);
return parse_task(c);
}
bool Config::parse(const char* fn) {
std::ifstream fs(fn);
if (!fs.is_open()) {
LOG_ERRNO("cannot open '%s' for reading", fn);
return false;
}
unsigned lno = 0;
std::string line;
while (getline(fs, line)) {
lno++;
if (!parse_line(line)) {
LOG("cannot parse line %u", lno);
fs.close();
servers.clear();
matches.clear();
return false;
}
}
if (fs.bad()) {
LOG_ERRNO("cannot read from '%s'", fn);
servers.clear();
matches.clear();
return false;
}
fs.close();
for (std::map<std::string,server_t>::const_iterator it = servers.begin(); it != servers.end(); ++it) {
if (!it->second.tasks.empty()) {
return true;
}
}
LOG("nothing to do");
servers.clear();
matches.clear();
return false;
}