aboutsummaryrefslogtreecommitdiff
path: root/pop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pop.cpp')
-rw-r--r--pop.cpp252
1 files changed, 252 insertions, 0 deletions
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 <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);
+ }
+}