From aa4d426b4d3527d7e166df1a05058c9a4a0f6683 Mon Sep 17 00:00:00 2001 From: Wojtek Kosior Date: Fri, 30 Apr 2021 00:33:56 +0200 Subject: initial/final commit --- pop.cpp | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 252 insertions(+) create mode 100644 pop.cpp (limited to 'pop.cpp') diff --git a/pop.cpp b/pop.cpp new file mode 100644 index 0000000..3407d1c --- /dev/null +++ b/pop.cpp @@ -0,0 +1,252 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 handler, + std::set *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 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 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 session = + vmime::net::session::create(); + vmime::shared_ptr store; + vmime::shared_ptr folder; + vmime::shared_ptr 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 + (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); + } +} -- cgit v1.2.3