#include <string.h>
#include <unistd.h>
#include <iostream>
#include <fstream>
#include <set>
#include <regex>
#include <io.h>
#include <direct.h>
#include "vmime/vmime.hpp"
#include "utilities.h"
#include "timeout_handler.hpp"
#include "exceptions.hpp"
extern "C" void validate_config(struct config *config)
{
if (config->use_SSL == PARAM_NOT_GIVEN)
config->use_SSL = PARAM_NO;
if (config->use_AUTH == PARAM_NOT_GIVEN)
config->use_AUTH = PARAM_NO;
if (config->save_body == PARAM_NOT_GIVEN)
config->save_body = PARAM_YES;
if (config->save_subj == PARAM_NOT_GIVEN)
config->save_subj = PARAM_YES;
if (config->del_msgs == PARAM_NOT_GIVEN)
config->del_msgs = PARAM_NO;
if (is_empty_string(config->host))
FAIL(MSG_BAD_VAR, "SERVER:host");
if (!is_valid_port_number(config->port_POP3))
FAIL(MSG_BAD_PORT_VAR, "SERVER:port_pop3");
if (!config->password)
FAIL(MSG_BAD_VAR, "SERVER:password");
if (is_empty_string(config->login))
FAIL(MSG_BAD_VAR, "SERVER:login");
if (is_empty_string(config->dest_dir))
FAIL(MSG_BAD_VAR, "SERVER:outputdir");
if (config->dest_dir[strlen(config->dest_dir) - 1] != '\\')
FAIL(MSG_BAD_DEST_DIR_END);
if (access(config->dest_dir, F_OK) != 0)
FAIL(MSG_DEST_DIR_NOT_EXIST);
if (access(config->dest_dir, W_OK) != 0)
FAIL(MSG_DEST_DIR_NOT_WRITEABLE);
}
static const char mail_dir_base[] = "mail_";
static const int mail_base_length = sizeof(mail_dir_base) - 1;
static long deduct_mail_dir_number(const char *parent_dir_path)
{
size_t path_length = strlen(parent_dir_path);
intptr_t search_group;
_finddata_t find_data;
long max_number = 0, parsed_number;
char *glob = (char*) dirtyalloc(path_length + mail_base_length + 2);
const char *glob_match_start;
memcpy(glob, parent_dir_path, path_length);
memcpy(glob + path_length, mail_dir_base, mail_base_length);
path_length += mail_base_length;
strcpy(glob + path_length, "*");
search_group = _findfirst(glob, &find_data);
if (search_group < 0)
goto after_searching;
do {
glob_match_start = find_data.name + mail_base_length;
if (*glob_match_start < '1' || *glob_match_start > '9')
continue;
parsed_number = atol(glob_match_start);
if (parsed_number > max_number)
max_number = parsed_number;
} while (_findnext(search_group, &find_data) == 0);
_findclose(search_group);
after_searching:
free(glob);
return max_number + 1;
}
static std::string get_next_filename(std::string filename)
{
std::regex
numbered_reg("(.[^.]*)[(]([1-9][0-9]{0,8})[)](([.].*)?)"),
unnumbered_reg("(.[^.]*)(([.].*)?)");
std::smatch what;
std::string base_name, number, rest;
long new_idx;
if (regex_match(filename, what, numbered_reg)) {
base_name = what[1];
number = what[2];
rest = what[3];
new_idx = std::stol(number) + 1;
} else {
regex_match(filename, what, unnumbered_reg);
base_name = what[1];
rest = what[2];
new_idx = 1;
}
return base_name + "(" + std::to_string(new_idx) + ")" + rest;
}
static int save_file_from_handler
(std::string dir_path, std::string file_name,
vmime::shared_ptr<const vmime::contentHandler> handler,
std::set<std::string> *filenames_used)
{
while (filenames_used->count(file_name))
file_name = get_next_filename(file_name);
std::ofstream write;
write.open(dir_path + file_name, std::ios_base::binary);
vmime::utility::outputStreamAdapter
write_adapter(write);
handler->extract(write_adapter);
filenames_used->insert(file_name);
if (!write.good()) {
fprintf(stderr, MSG_FILE_SAVE_FAIL, dir_path + file_name);
fputs("\r\n", stderr);
return -1;
}
return 0;
}
static void save_message(const struct config *config, long dir_number,
vmime::shared_ptr<vmime::net::message> msg,
long *saved_files_count)
{
size_t path_length = strlen(config->dest_dir);
char *dir_path =
(char*) dirtyalloc(path_length + mail_base_length + 25);
memcpy(dir_path, config->dest_dir, path_length);
memcpy(dir_path + path_length, mail_dir_base, mail_base_length);
path_length += mail_base_length;
path_length += sprintf(dir_path + path_length, "%ld\\", dir_number);
if (_mkdir(dir_path) != 0) {
PRINT_ERROR(MSG_MKDIR_FAIL, dir_path);
throw silent_exception();
}
std::string dir_path_str = std::string(dir_path);
std::set<std::string> filenames_used;
vmime::messageParser mp(msg->getParsedMessage());
if (config->save_subj) {
std::ofstream write_subj;
write_subj.open(dir_path_str + "temat.txt");
write_subj << mp.getSubject().getWholeBuffer();
filenames_used.insert("temat.txt");
}
if (config->save_body) {
std::string file_name;
for (auto text_part : mp.getTextPartList()) {
if (text_part->getType().getType() == "text/html")
file_name = "tresc.html";
else
file_name = "tresc.txt";
save_file_from_handler(dir_path_str, file_name,
text_part->getText(),
&filenames_used);
}
}
for (auto attachment : mp.getAttachmentList()) {
std::string file_name = attachment->getName().getBuffer();
if (save_file_from_handler(dir_path_str, file_name,
attachment->getData(),
&filenames_used) == 0)
(*saved_files_count)++;
}
free(dir_path);
}
using namespace vmime::security::cert;
static void _perform_work(const struct config *config, long *saved_messages,
long *saved_files, long *first_mail_dir_number)
{
vmime::shared_ptr<vmime::net::session> session =
vmime::net::session::create();
vmime::shared_ptr<vmime::net::store> store;
vmime::shared_ptr<vmime::net::folder> folder;
vmime::shared_ptr <vmime::net::message> msg;
long left;
std::string proto = config->use_SSL ? "pop3s" : "pop3";
session->getProperties().setProperty("store.protocol", proto);
session->getProperties().setProperty
("store." + proto + ".server.address", std::string(config->host));
session->getProperties().setProperty("server.port", config->port_POP3);
session->getProperties().setProperty
("store." + proto + ".auth.password", std::string(config->password));
session->getProperties().setProperty
("store." + proto + ".auth.username", std::string(config->login));
store = session->getStore();
if (config->use_SSL)
store->setCertificateVerifier(windows_root_certs_verifier());
store->setTimeoutHandlerFactory
(vmime::make_shared<parameterized_timeout_handler_factory>
(config->timeout));
store->connect();
folder = store->getDefaultFolder();
folder->open(vmime::net::folder::MODE_READ_WRITE);
*first_mail_dir_number = deduct_mail_dir_number(config->dest_dir);
long nmes = folder->getMessageCount();
for (left = nmes /*folder->getMessageCount()*/; left > 0; left--) {
msg = folder->getMessage(/*1*/nmes - left + 1);
save_message(config, *first_mail_dir_number + *saved_messages,
msg, saved_files);
(*saved_messages)++;
if (config->del_msgs) {
folder->deleteMessages
(vmime::net::messageSet::byNumber(nmes - left + 1));
}
}
folder->close(true);
store->disconnect();
}
extern "C" void perform_work(const struct config *config)
{
long saved_messages = 0, saved_files = 0, first_mail_dir_number = 0;
#define PRINT_STATS() \
if (first_mail_dir_number > 0) \
(std::cout << "[" << saved_messages << "\\" << saved_files \
<< "\\" << first_mail_dir_number << "]" << std::endl)
try {
_perform_work(config, &saved_messages,
&saved_files, &first_mail_dir_number);
PRINT_STATS();
SUCCEED();
} catch (vmime::exception &e) {
PRINT_STATS();
handle_vmime_exception(&e);
} catch (std::exception &e) {
PRINT_STATS();
handle_std_exception(&e);
}
}