#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); } }