
Добавил:
CanyonE
СПбГУТ * ИКСС * Программная инженерия
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:8 семестр / Готовая курсовая работа / MailClient / mainwindow
.cpp#include <QInputDialog>
#include <QMessageBox>
#include "mainwindow.h"
#include "send_message_dialog.h"
#include "settings_dialog.h"
#include "tools.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
ui->folders_tree->header()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);
ui->folders_tree->setSortingEnabled(true);
ui->folders_tree->sortByColumn(0, Qt::AscendingOrder);
ui->messages_table->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
ui->messages_table->setSortingEnabled(true);
ui->messages_table->sortByColumn(2, Qt::DescendingOrder);
// imap initial settings
imap_settings.set_server_address("imap.yandex.ru");
imap_settings.set_server_port(993);
imap_settings.set_login("robert.wayne.alx@yandex.ru");
imap_settings.set_password("Robw12345");
imap_settings.set_autoupdate(false);
imap_settings.set_autoupdate_interval(10);
imap_settings.set_auth_method(mailio::imaps::auth_method_t::LOGIN);
// smtp initial settings
smtp_settings.set_server_address("smtp.yandex.ru");
smtp_settings.set_server_port(465);
smtp_settings.set_login("robert.wayne.alx@yandex.ru");
smtp_settings.set_password("Robw12345");
smtp_settings.set_name("robert.wayne.alx");
smtp_settings.set_auth_method(mailio::smtps::auth_method_t::LOGIN);
// other
ui->messages_table->hideColumn(ui->messages_table->columnCount() - 1);
ui->folders_tree->hideColumn(ui->folders_tree->columnCount() - 1);
ui->horizontal_splitter->setStretchFactor(0, 0);
ui->horizontal_splitter->setStretchFactor(1, 1);
this->update_status = true;
// threads for update folders and messages
thread_for_auto_update = new ThreadForAutoUpdate(this, &imap_settings);
connect(thread_for_auto_update, SIGNAL(folders_and_messages_updated()), this,
SLOT(update_folders_and_messages_ui()));
thread_for_manual_update = new ThreadForManualUpdate(this);
connect(thread_for_manual_update, SIGNAL(folders_and_messages_updated()), this,
SLOT(update_folders_and_messages_ui()));
thread_for_auto_update->start();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::update_folders_and_messages()
{
try
{
this->start = std::chrono::system_clock::now();
mailio::imaps conn = mailio::imaps(imap_settings.get_server_address(),
static_cast<unsigned>(imap_settings.get_server_port()), timeout);
conn.authenticate(imap_settings.get_login(), imap_settings.get_password(), imap_settings.get_auth_method());
mailio::imaps::mailbox_folder_t mailbox_main_folder = conn.list_folders("");
std::unordered_map<std::string, unsigned long> new_messages_ids_to_uids;
std::unordered_map<unsigned long, std::string> new_uids_to_messages_ids;
std::map<std::list<std::string>, std::unordered_set<std::string>> new_mailboxes_to_messages_ids;
QTreeWidgetItem root;
list_folders_recursive(&root, mailbox_main_folder, conn, std::list<std::string>(), this->messages,
this->messages_sizes, new_mailboxes_to_messages_ids, new_messages_ids_to_uids,
new_uids_to_messages_ids);
QList<QTreeWidgetItem *> new_folders;
for (size_t i = 0, n = root.childCount(); i < n; ++i)
{
new_folders.emplace_back(root.child(i)->clone());
}
this->messages_ids_to_uids = new_messages_ids_to_uids;
this->uids_to_messages_ids = new_uids_to_messages_ids;
this->mailboxes_to_messages_ids = new_mailboxes_to_messages_ids;
this->folders.swap(new_folders);
this->update_status = true;
this->update_status_message.clear();
}
catch (const std::exception &exc)
{
this->update_status = false;
this->update_status_message = "Список папок с сообщениями не обновлен: " + std::string(exc.what());
}
}
void MainWindow::list_folders_recursive(
QTreeWidgetItem *twi, const mailio::imaps::mailbox_folder_t &folder, mailio::imaps &conn,
const std::list<std::string> &path, std::unordered_map<std::string, mailio::message> &messages,
std::unordered_map<std::string, unsigned long long> &messages_sizes,
std::map<std::list<std::string>, std::unordered_set<std::string>> &mailboxes_to_messages_ids,
std::unordered_map<std::string, unsigned long> &messages_ids_to_uids,
std::unordered_map<unsigned long, std::string> &uids_to_messages_ids)
{
for (auto &folder : folder.folders)
{
try
{
std::list<std::string> new_list(path);
new_list.emplace_back(folder.first);
mailboxes_to_messages_ids[new_list];
QTreeWidgetItem *child = new QTreeWidgetItem();
twi->addChild(child);
child->setText(0, utf7decode(QString::fromStdString(folder.first)));
child->setText(3, QString::fromStdString(folder.first));
if (!folder.second.folders.empty())
{
list_folders_recursive(child, folder.second, conn, new_list, messages, messages_sizes,
mailboxes_to_messages_ids, messages_ids_to_uids, uids_to_messages_ids);
}
mailio::imaps::mailbox_stat_t stat;
try
{
stat = conn.select(new_list, true);
}
catch (...)
{
stat.messages_unseen = 0;
stat.messages_no = 0;
}
child->setText(1, QString::number(stat.messages_unseen));
child->setText(2, QString::number(stat.messages_no));
std::list<mailio::imaps::messages_range_t> ranges;
ranges.emplace_back(mailio::imaps::messages_range_t(1, std::optional<unsigned long>(-1)));
std::map<unsigned long, mailio::message> found_messages;
try
{
conn.fetch(ranges, found_messages, true, true, mailio::codec::line_len_policy_t::VERYLARGE);
}
catch (...)
{
child->setText(1, "-");
child->setText(2, "-");
continue;
}
if (found_messages.empty())
{
child->setText(1, "0");
child->setText(2, "0");
continue;
}
for (auto message_it = found_messages.begin(); message_it != found_messages.end(); ++message_it)
{
try
{
unsigned long uid = message_it->first;
mailio::message current_message = message_it->second;
std::string message_id = current_message.message_id();
messages_ids_to_uids[message_id] = uid;
uids_to_messages_ids[uid] = message_id;
mailboxes_to_messages_ids[new_list].insert(message_id);
if (messages.find(message_id) == messages.end())
{
// update if empty
messages[message_id] = current_message;
std::string message_source;
current_message.format(message_source);
messages_sizes[message_id] = message_source.size();
// try to get full message (may except)
try
{
conn.fetch(uid, current_message, true, false);
current_message.format(message_source, false);
QString qmessage_source = QString::fromStdString(message_source);
qmessage_source.replace("This is a MIME-encapsulated message.", "");
message_source = qmessage_source.toStdString();
messages[message_id].parse(message_source, false);
messages_sizes[message_id] = message_source.size();
if (current_message.parts().empty())
{
std::string::size_type pos = message_source.find("\r\n\r\n");
if (pos == std::string::npos)
{
continue;
}
std::string content = message_source.substr(pos + 4, std::string::npos);
messages[message_id].content(content);
}
}
catch (...)
{
// try another way
std::string new_message_id(message_id);
while (new_message_id.size() > 2)
{
new_message_id.assign(new_message_id.begin() + 1, new_message_id.end() - 1);
if (messages.find(new_message_id) != messages.end())
{
messages[new_message_id].format(message_source, false);
messages[message_id].parse(message_source, false);
messages_sizes[message_id] = message_source.size();
break;
}
}
}
}
}
catch (...)
{
}
}
}
catch (...)
{
}
}
}
void MainWindow::update_folders_and_messages_ui()
{
if (!this->update_status)
{
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + this->update_status_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
return;
}
try
{
std::string update_message;
bool ok = false;
try
{
ui->folders_tree->clear();
ui->folders_tree->addTopLevelItems(this->folders);
ui->folders_tree->expandAll();
int width = 0;
for (int i = 0, n = ui->folders_tree->columnCount(); i < n; ++i)
{
width += ui->folders_tree->columnWidth(i);
}
ui->horizontal_splitter->setSizes(QList<int>{width + 10, ui->centralwidget->width() - width - 10});
std::chrono::duration<double> sec = std::chrono::system_clock::now() - this->start;
update_message = "Список папок с сообщениями обновлен, сообщения с вложениями доступны оффлайн (" +
std::to_string(sec.count()) + " сек.)";
ok = true;
}
catch (const std::exception &exc)
{
update_message = "Список папок с сообщениями не обновлен: " + std::string(exc.what());
}
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
if (ok)
{
folder_chosen_ui();
}
}
catch (const std::exception &exc)
{
QString log_message =
QString::fromStdString("Ошибка обновления списка папок с сообщениями: " + std::string(exc.what()));
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
}
}
void MainWindow::folder_chosen_ui()
{
try
{
QTreeWidgetItem *current_folder = ui->folders_tree->currentItem();
std::list<std::string> folder_name;
if (current_folder == nullptr)
{
if (this->current_item.empty())
{
return;
}
folder_name = this->current_item;
}
else
{
QTreeWidgetItem *current = current_folder;
while (current)
{
folder_name.emplace_front(current->text(3).toStdString());
current = current->parent();
}
this->current_item = folder_name;
}
unsigned long index = 0, n_messages = 0;
std::list<std::tuple<unsigned long, unsigned long, QTableWidgetItem *>> cells;
std::unordered_set<std::string> current_messages = this->mailboxes_to_messages_ids[folder_name];
constexpr unsigned long long kb = 1 << 10, mb = kb << 10, gb = mb << 10;
for (auto it = current_messages.begin(); it != current_messages.end(); ++it, ++index)
{
try
{
unsigned long uid = this->messages_ids_to_uids[*it];
mailio::message current_message = this->messages[*it];
std::string message_id = current_message.message_id();
QTableWidgetItem *r0item, *r1item, *r2item, *r3item, *r4item, *r5item, *r6item, *r7item;
try
{
r0item = new QTableWidgetItem(QString::fromStdString(current_message.subject()).simplified());
}
catch (...)
{
r0item = new QTableWidgetItem("[undefined]");
}
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 0, r0item));
++n_messages;
try
{
r1item =
new QTableWidgetItem(QString::fromStdString(current_message.from_to_string()).simplified());
}
catch (...)
{
r1item = new QTableWidgetItem("[undefined]");
}
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 1, r1item));
try
{
r2item = new QTableWidgetItem(
QString::fromStdString(local_date_time_as_string(current_message.date_time())));
}
catch (...)
{
r2item = new QTableWidgetItem("[undefined]");
}
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 2, r2item));
try
{
r3item = new QTableWidgetItem(
QString::fromStdString(current_message.recipients_to_string()).simplified());
}
catch (...)
{
r3item = new QTableWidgetItem("[undefined]");
}
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 3, r3item));
try
{
r4item = new QTableWidgetItem(QString::number(current_message.attachments_size()));
}
catch (...)
{
r4item = new QTableWidgetItem("[undefined]");
}
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 4, r4item));
try
{
unsigned long long size = this->messages_sizes[message_id];
std::string size_s;
if (size >= gb)
{
size = size * 100 / gb;
size_s = std::to_string(size / 100) + "." + std::to_string(size % 100) + " ГиБ";
}
else if (size >= mb)
{
size = size * 100 / mb;
size_s = std::to_string(size / 100) + "." + std::to_string(size % 100) + " МиБ";
}
else if (size >= kb)
{
size = size * 100 / kb;
size_s = std::to_string(size / 100) + "." + std::to_string(size % 100) + " КиБ";
}
else
{
size_s = std::to_string(size) + " Б";
}
r5item = new QTableWidgetItem(QString::fromStdString(size_s));
}
catch (...)
{
r5item = new QTableWidgetItem("[undefined]");
}
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 5, r5item));
try
{
r6item = new QTableWidgetItem(QString::number(uid));
}
catch (...)
{
r6item = new QTableWidgetItem("[undefined]");
}
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 6, r6item));
r7item = new QTableWidgetItem(QString::fromStdString(*it));
cells.emplace_back(std::tuple<unsigned long, unsigned long, QTableWidgetItem *>(index, 7, r7item));
if (this->messages_ids_to_text_documents.find(message_id) == this->messages_ids_to_text_documents.end())
{
QString all_parts;
std::vector<mailio::mime> mimes = current_message.parts();
if (mimes.empty())
{
all_parts += QString::fromStdString(current_message.content());
}
for (const mailio::mime &mime : mimes)
{
mailio::mime::content_disposition_t content_disposition(mime.content_disposition());
if (content_disposition != mailio::mime::content_disposition_t::ATTACHMENT)
{
mailio::message::content_type_t content_type(mime.content_type());
if (content_type.type == mailio::mime::media_type_t::TEXT && content_type.subtype == "html")
{
all_parts += QString::fromStdString(mime.content());
}
else
{
all_parts +=
QTextDocumentFragment::fromPlainText(QString::fromStdString(mime.content()))
.toHtml();
}
}
}
this->messages_ids_to_text_documents[message_id].setHtml(all_parts);
}
}
catch (const std::exception &exc)
{
ui->logs_text_edit->appendPlainText(
QString::fromStdString("Ошибка чтения сообщения " + *it + ": " + std::string(exc.what())));
}
}
ui->messages_table->setSortingEnabled(false);
ui->messages_table->setRowCount(0);
ui->messages_table->setRowCount(n_messages);
unsigned long row, col;
QTableWidgetItem *item;
for (auto cell : cells)
{
std::tie(row, col, item) = cell;
ui->messages_table->setItem(row, col, item);
}
ui->messages_table->horizontalHeader()->resizeSections(QHeaderView::ResizeMode::ResizeToContents);
ui->messages_table->setSortingEnabled(true);
}
catch (const std::exception &exc)
{
QString log_message =
QString::fromStdString("Ошибка чтения сообщений выбранной папки: " + std::string(exc.what()));
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
}
}
void MainWindow::on_update_folders_and_messages_triggered()
{
if (!thread_for_manual_update->isRunning())
{
thread_for_manual_update->start();
}
}
void MainWindow::on_set_up_account_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
SettingsDialog new_settings_dialog;
new_settings_dialog.set_imap_settings(imap_settings);
new_settings_dialog.set_smtp_settings(smtp_settings);
if (new_settings_dialog.exec() == QDialog::Accepted)
{
ImapSettings new_imap_settings = new_settings_dialog.get_imap_settings();
SmtpSettings new_smtp_settings = new_settings_dialog.get_smtp_settings();
imap_settings = new_imap_settings;
smtp_settings = new_smtp_settings;
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_exit_program_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
if (QMessageBox::question(this, "Внимание", "Вы уверены, что хотите выйти?", QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) == QMessageBox::Yes)
{
clear_all();
exit(0);
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_add_folder_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
std::list<std::string> folder_name_decoded;
QTreeWidgetItem *current = ui->folders_tree->currentItem();
while (current)
{
folder_name_decoded.emplace_front(current->text(0).toStdString());
current = current->parent();
}
std::string folder_name_s;
for (auto s : folder_name_decoded)
{
folder_name_s += "\\" + s;
}
folder_name_decoded.clear();
bool ok = false;
std::string update_message;
QString text = QInputDialog::getText(this, "Создание новой папки", "Название папки:", QLineEdit::Normal,
QString::fromStdString(folder_name_s), &ok);
if (ok && !text.isEmpty())
{
std::list<std::string> new_folder_name;
std::string new_folder_name_s;
for (auto s : text.split('\\'))
{
if (!s.isEmpty())
{
new_folder_name.emplace_back(utf7encode(s).toStdString());
new_folder_name_s += "\\" + s.toStdString();
}
}
try
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
mailio::imaps conn(imap_settings.get_server_address(),
static_cast<unsigned>(imap_settings.get_server_port()), timeout);
conn.authenticate(imap_settings.get_login(), imap_settings.get_password(),
imap_settings.get_auth_method());
ok = conn.create_folder(new_folder_name);
if (!ok)
{
throw mailio::imap_error("отказано в операции сервером");
}
std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
update_message = "Папка " + new_folder_name_s + " добавлена (" + std::to_string(sec.count()) + " сек.)";
}
catch (const std::exception &exc)
{
update_message = "Папка " + new_folder_name_s + " не добавлена: " + std::string(exc.what());
}
}
else
{
update_message = "Папка не добавлена: отмена операции";
}
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
if (ok)
{
main_mutex.unlock();
on_update_folders_and_messages_triggered();
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_rename_folder_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
if (ui->folders_tree->currentItem() == nullptr)
{
QMessageBox::warning(this, "Внимание", "Папка не выбрана");
return;
}
std::list<std::string> old_folder_name, old_folder_name_decoded;
QTreeWidgetItem *current = ui->folders_tree->currentItem();
while (current)
{
old_folder_name_decoded.emplace_front(current->text(0).toStdString());
old_folder_name.emplace_front(current->text(3).toStdString());
current = current->parent();
}
std::string old_folder_name_s;
for (auto s : old_folder_name_decoded)
{
old_folder_name_s += "\\" + s;
}
bool ok = false;
std::string update_message;
QString text =
QInputDialog::getText(this, "Переименование/перемещение папки", "Новое название папки:", QLineEdit::Normal,
QString::fromStdString(old_folder_name_s), &ok);
if (ok && !text.isEmpty())
{
try
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
mailio::imaps conn(imap_settings.get_server_address(),
static_cast<unsigned>(imap_settings.get_server_port()), timeout);
conn.authenticate(imap_settings.get_login(), imap_settings.get_password(),
imap_settings.get_auth_method());
std::list<std::string> new_folder_name;
std::string new_folder_name_s;
for (auto s : text.split('\\'))
{
if (!s.isEmpty())
{
new_folder_name.emplace_back(utf7encode(s).toStdString());
new_folder_name_s += "\\" + s.toStdString();
}
}
ok = conn.rename_folder(old_folder_name, new_folder_name);
if (!ok)
{
throw mailio::imap_error("отказано в операции сервером");
}
std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
update_message = "Папка " + old_folder_name_s + " переименована -> " + new_folder_name_s + " (" +
std::to_string(sec.count()) + " сек.)";
}
catch (const std::exception &exc)
{
update_message = "Папка " + old_folder_name_s + " не переименована: " + std::string(exc.what());
}
}
else
{
update_message = "Папка " + old_folder_name_s + " не переименована: отмена операции";
}
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
if (ok)
{
main_mutex.unlock();
on_update_folders_and_messages_triggered();
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_delete_selected_folder_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
std::list<std::string> folder_name, folder_name_decoded;
std::string folder_name_s;
QTreeWidgetItem *current = ui->folders_tree->currentItem();
while (current)
{
folder_name_decoded.emplace_front(current->text(0).toStdString());
folder_name.emplace_front(current->text(3).toStdString());
current = current->parent();
}
for (auto s : folder_name_decoded)
{
folder_name_s += "\\" + s;
};
std::string update_message;
bool ok = (QMessageBox::question(
this, "Внимание",
QString::fromStdString("Вы уверены, что хотите удалить папку " + folder_name_s + "?"),
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes);
if (ok)
{
try
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
mailio::imaps conn(imap_settings.get_server_address(),
static_cast<unsigned>(imap_settings.get_server_port()), timeout);
conn.authenticate(imap_settings.get_login(), imap_settings.get_password(),
imap_settings.get_auth_method());
ok = conn.delete_folder(folder_name);
if (!ok)
{
throw mailio::imap_error("отказано в операции сервером");
}
std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
update_message = "Папка " + folder_name_s + " удалена (" + std::to_string(sec.count()) + " сек.)";
}
catch (const std::exception &exc)
{
update_message = "Папка " + folder_name_s + " не удалена: " + std::string(exc.what());
}
}
else
{
update_message = "Папка " + folder_name_s + " не удалена: отмена операции";
}
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
if (ok)
{
main_mutex.unlock();
on_update_folders_and_messages_triggered();
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_send_message_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
mailio::smtps conn(smtp_settings.get_server_address(), smtp_settings.get_server_port());
conn.authenticate(smtp_settings.get_login(), smtp_settings.get_password(), smtp_settings.get_auth_method());
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
return;
}
SendMessageDialog new_send_message_dialog;
try
{
while (new_send_message_dialog.exec() == QDialog::Accepted)
{
try
{
mailio::message message = new_send_message_dialog.get_message();
if (message.subject().empty())
{
QMessageBox::warning(this, "Ошибка", "Тема сообщения пуста");
continue;
}
if (message.recipients().empty() && message.cc_recipients().empty() && message.bcc_recipients().empty())
{
QMessageBox::warning(this, "Ошибка", "Получатели отсутствуют");
continue;
}
message.from(mailio::mail_address(smtp_settings.get_name(), smtp_settings.get_login()));
mailio::smtps conn(smtp_settings.get_server_address(), smtp_settings.get_server_port());
conn.authenticate(smtp_settings.get_login(), smtp_settings.get_password(),
smtp_settings.get_auth_method());
std::string status = conn.submit(message);
std::string update_message = "Сообщение отправлено, обновите "
"список папок с сообщениями";
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
QMessageBox::information(this, "Информация",
"Сообщение отправлено\nОбновите список папок "
"с сообщениями\n\nОтвет сервера:\n" +
QString::fromStdString(status));
break;
}
catch (const std::exception &exc)
{
QMessageBox::warning(this, "Ошибка", exc.what());
}
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_download_attachment_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
QTableWidgetItem *current_item = ui->messages_table->currentItem();
if (current_item == nullptr)
{
QMessageBox::warning(this, "Внимание", "Сообщение не выбрано");
return;
}
std::string current_message_id =
ui->messages_table->item(ui->messages_table->currentRow(), 7)->text().toStdString();
if (messages.find(current_message_id) == messages.end())
{
QMessageBox::warning(this, "Внимание", "Сообщение не найдено");
return;
}
mailio::message current_message = messages[current_message_id];
size_t n_attachments = current_message.attachments_size();
if (n_attachments <= 0)
{
QMessageBox::warning(this, "Внимание", "Вложения в сообщении не найдены");
return;
}
QStringList attachment_names, attachment_names_and_sizes;
std::vector<std::shared_ptr<std::stringstream>> attachments_data_streams;
constexpr unsigned long long kb = 1 << 10, mb = kb << 10, gb = mb << 10;
for (size_t i = 1; i <= n_attachments; ++i)
{
std::string attachment_name, attachment_size;
std::shared_ptr<std::stringstream> ss = std::make_shared<std::stringstream>();
std::istream &is = *ss;
std::ostream &os = *ss;
current_message.attachment(i, os, attachment_name);
is.seekg(0, std::ios::end);
unsigned long long size = is.tellg();
is.seekg(0, std::ios::beg);
if (size >= gb)
{
size = size * 100 / gb;
attachment_size = std::to_string(size / 100) + "." + std::to_string(size % 100) + " ГиБ";
}
else if (size >= mb)
{
size = size * 100 / mb;
attachment_size = std::to_string(size / 100) + "." + std::to_string(size % 100) + " МиБ";
}
else if (size >= kb)
{
size = size * 100 / kb;
attachment_size = std::to_string(size / 100) + "." + std::to_string(size % 100) + " КиБ";
}
else
{
attachment_size = std::to_string(size) + " Б";
}
attachment_names.emplace_back(QString::fromStdString(attachment_name));
attachment_names_and_sizes.emplace_back(
QString::fromStdString(attachment_name + " (" + attachment_size + ")"));
attachments_data_streams.emplace_back(ss);
}
bool ok = false;
std::string update_message;
QString filename_with_size = QInputDialog::getItem(this, "Внимание", "Какое вложение вы хотите скачать?",
attachment_names_and_sizes, 0, false, &ok);
if (ok)
{
size_t p = 0;
if ((p = attachment_names_and_sizes.indexOf(filename_with_size)) < 0)
{
QMessageBox::warning(this, "Внимание", "Вложение не найдено");
return;
}
QString new_filename = QInputDialog::getText(this, "Внимание", "Введите путь и/или имя файла для записи",
QLineEdit::Normal, attachment_names[p], &ok);
if (ok)
{
try
{
std::ofstream file_out(new_filename.toStdWString().c_str(), std::ios::out | std::ios::binary);
file_out << ((std::istream &)*attachments_data_streams[p]).rdbuf();
file_out.close();
update_message = "Файл " + new_filename.toStdString() + " записан";
}
catch (const std::exception &exc)
{
update_message = "Ошибка записи файла на диск: " + std::string(exc.what());
}
}
else
{
update_message = "Сохранение файла отменено";
}
}
else
{
update_message = "Сохранение файла отменено";
}
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_copy_selected_message_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
QItemSelectionModel *select = ui->messages_table->selectionModel();
if (!select->hasSelection())
{
QMessageBox::warning(this, "Внимание", "Сообщения не выбраны");
return;
}
QList<QString> folders;
for (auto folder : mailboxes_to_messages_ids)
{
QString folder_to_emplace;
for (auto s : folder.first)
{
folder_to_emplace += "\\" + utf7decode(QString::fromStdString(s));
}
folders.emplace_back(folder_to_emplace);
}
bool ok = false;
QString text =
QInputDialog::getItem(this, "Копирование сообщений в папку", "Название папки:", folders, 0, false, &ok);
if (!ok || text.isEmpty())
{
return;
}
std::list<std::string> destination_folder_name;
for (auto s : text.split('\\'))
{
if (!s.isEmpty())
{
destination_folder_name.emplace_back(utf7encode(s).toStdString());
}
}
try
{
std::string update_message;
QModelIndexList selection = select->selectedIndexes();
if (selection.count() == 0)
{
QMessageBox::warning(this, "Внимание", "Сообщения не выбраны");
return;
}
std::unordered_set<int> rows;
for (qsizetype i = 0; i < selection.count(); i++)
{
rows.insert(selection.at(i).row());
}
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
mailio::imaps conn(imap_settings.get_server_address(),
static_cast<unsigned>(imap_settings.get_server_port()), timeout);
conn.authenticate(imap_settings.get_login(), imap_settings.get_password(), imap_settings.get_auth_method());
for (auto row : rows)
{
QTableWidgetItem *current_uid = ui->messages_table->item(row, 6);
if (current_uid == nullptr || current_uid->text().isEmpty())
{
QMessageBox::warning(this, "Внимание", "UID одного сообщения не найден");
continue;
}
ok = false;
unsigned long uid = current_uid->text().toULong(&ok);
if (!ok)
{
QMessageBox::warning(this, "Внимание", "Неверный UID у одного сообщения");
continue;
}
QTableWidgetItem *current_message_id = ui->messages_table->item(row, 7);
if (current_message_id == nullptr || current_message_id->text().isEmpty())
{
QMessageBox::warning(this, "Внимание", "Message ID одного сообщения не найден");
continue;
}
std::string current_message_id_s = current_message_id->text().toStdString();
std::string datetime_s = ui->messages_table->item(row, 2)->text().toStdString();
try
{
conn.append(destination_folder_name, this->messages[current_message_id_s]);
std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
update_message = "Сообщение #" + std::to_string(uid) + " (" + datetime_s + ") скопировано (" +
std::to_string(sec.count()) + " сек.)";
}
catch (const std::exception &exc)
{
update_message = "Сообщение #" + std::to_string(uid) + " (" + datetime_s +
") не скопировано: " + std::string(exc.what());
}
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
ui->current_message->setDocument(&this->empty_text_document);
}
main_mutex.unlock();
on_update_folders_and_messages_triggered();
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_delete_selected_message_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
QTreeWidgetItem *current_folder = ui->folders_tree->currentItem();
std::list<std::string> folder_name;
if (current_folder == nullptr)
{
if (this->current_item.empty())
{
QMessageBox::warning(this, "Внимание", "Папка не выбрана");
return;
}
folder_name = this->current_item;
}
else
{
QTreeWidgetItem *current = current_folder;
while (current)
{
folder_name.emplace_front(current->text(3).toStdString());
current = current->parent();
}
this->current_item = folder_name;
}
QItemSelectionModel *select = ui->messages_table->selectionModel();
if (!select->hasSelection())
{
QMessageBox::warning(this, "Внимание", "Сообщения не выбраны");
return;
}
bool ok = (QMessageBox::question(this, "Внимание", "Вы уверены, что хотите удалить выбранные сообщения?",
QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes);
if (!ok)
{
return;
}
try
{
std::string update_message;
QModelIndexList selection = select->selectedIndexes();
if (selection.count() == 0)
{
QMessageBox::warning(this, "Внимание", "Сообщения не выбраны");
return;
}
std::unordered_set<int> rows;
for (qsizetype i = 0; i < selection.count(); i++)
{
rows.insert(selection.at(i).row());
}
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
mailio::imaps conn(imap_settings.get_server_address(),
static_cast<unsigned>(imap_settings.get_server_port()), timeout);
conn.authenticate(imap_settings.get_login(), imap_settings.get_password(), imap_settings.get_auth_method());
for (auto row : rows)
{
QTableWidgetItem *current_uid = ui->messages_table->item(row, 6);
if (current_uid == nullptr || current_uid->text().isEmpty())
{
QMessageBox::warning(this, "Внимание", "UID одного сообщения не найден");
continue;
}
ok = false;
unsigned long uid = current_uid->text().toULong(&ok);
if (!ok)
{
QMessageBox::warning(this, "Внимание", "Неверный UID у одного сообщения");
continue;
}
std::string datetime_s = ui->messages_table->item(row, 2)->text().toStdString();
try
{
conn.remove(folder_name, uid, true);
std::chrono::duration<double> sec = std::chrono::system_clock::now() - start;
update_message = "Сообщение #" + std::to_string(uid) + " (" + datetime_s + ") удалено (" +
std::to_string(sec.count()) + " сек.)";
}
catch (const std::exception &exc)
{
update_message = "Сообщение #" + std::to_string(uid) + " (" + datetime_s +
") не удалено: " + std::string(exc.what());
}
std::string current_datetime_s = get_current_datetime_as_string();
QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);
ui->status_bar->showMessage(log_message);
ui->logs_text_edit->appendPlainText(log_message);
ui->current_message->setDocument(&this->empty_text_document);
}
main_mutex.unlock();
on_update_folders_and_messages_triggered();
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_clear_all_and_disable_autoupdate_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
clear_all();
}
void MainWindow::on_show_message_statistics_triggered()
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
int n_folders = ui->folders_tree->topLevelItemCount(), n_messages = 0, n_unseen_messages = 0, n_attachments = 0;
for (int row = 0; row < ui->folders_tree->topLevelItemCount(); ++row)
{
n_folders += get_number_of_folders_recursive(ui->folders_tree->topLevelItem(row));
n_messages += ui->folders_tree->topLevelItem(row)->text(2).toInt();
n_unseen_messages += ui->folders_tree->topLevelItem(row)->text(1).toInt();
}
unsigned long long size = 0;
constexpr unsigned long long kb = 1 << 10, mb = kb << 10, gb = mb << 10;
for (auto message_size : this->messages_sizes)
{
size += message_size.second;
}
std::string size_s;
if (size >= gb)
{
size = size * 100 / gb;
size_s = std::to_string(size / 100) + "." + std::to_string(size % 100) + " ГиБ";
}
else if (size >= mb)
{
size = size * 100 / mb;
size_s = std::to_string(size / 100) + "." + std::to_string(size % 100) + " МиБ";
}
else if (size >= kb)
{
size = size * 100 / kb;
size_s = std::to_string(size / 100) + "." + std::to_string(size % 100) + " КиБ";
}
else
{
size_s = std::to_string(size) + " Б";
}
QString s = QString::fromStdString("Общее число папок, включая вложенные: " + std::to_string(n_folders) +
"\nОбщее число писем во всех папках: " + std::to_string(n_messages) +
"\nОбщее число непрочитанных писем: " + std::to_string(n_unseen_messages) +
"\nОбщий размер закэшированных сообщений с вложениями: " + size_s);
QMessageBox::information(this, "Статистика", s);
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
int MainWindow::get_number_of_folders_recursive(QTreeWidgetItem *twi)
{
int n = twi->childCount();
if (twi)
{
for (int c = 0, child_count = twi->childCount(); c < child_count; ++c)
{
n += get_number_of_folders_recursive(twi->child(c));
}
}
return n;
}
void MainWindow::on_show_about_window_triggered()
{
try
{
QMessageBox::about(this, "О программе",
"Курсовая работа по дисциплине ММиВА (СПбГУТ)\n"
"Тема: Разработка клиентского ПО для получения и "
"отправки почтовых сообщений\n"
"Выполнил студент группы ИКПИ-84\n"
"Коваленко Леонид\n"
"Санкт-Петербург\n"
"2022 год");
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_folders_tree_itemClicked(QTreeWidgetItem *,
int) // not use arguments because concurrent programming!
{
std::lock_guard<std::mutex> main_lock(main_mutex);
folder_chosen_ui();
}
void MainWindow::on_messages_table_itemClicked(QTableWidgetItem *)
{
std::lock_guard<std::mutex> main_lock(main_mutex);
try
{
QTableWidgetItem *current_message_id = ui->messages_table->item(ui->messages_table->currentRow(), 7);
if (current_message_id == nullptr || current_message_id->text().isEmpty())
{
ui->current_message->setDocument(&this->empty_text_document);
ui->status_bar->showMessage("Message ID выбранного сообщения не найден");
return;
}
std::string current_message_id_s = current_message_id->text().toStdString();
if (this->messages_ids_to_text_documents.find(current_message_id_s) ==
this->messages_ids_to_text_documents.end())
{
ui->current_message->setDocument(&this->empty_text_document);
ui->status_bar->showMessage("Тело выбранного сообщения не найдено");
return;
}
ui->current_message->setDocument(&this->messages_ids_to_text_documents[current_message_id_s]);
ui->status_bar->showMessage("Сообщение открыто");
}
catch (const std::exception &exc)
{
QMessageBox::critical(this, "Ошибка", exc.what());
}
}
void MainWindow::on_search_query_textChanged(const QString &query)
{
ui->messages_table->setCurrentCell(-1, -1);
if (query == "")
{
return;
}
std::lock_guard<std::mutex> main_lock(main_mutex);
auto find_items = ui->messages_table->findItems(query, Qt::MatchContains);
std::unordered_set<int> rows;
for (qsizetype i = 0, n = find_items.size(); i < n; ++i)
{
auto item = find_items.at(i);
item->setSelected(true);
rows.insert(item->row());
}
ui->status_bar->showMessage("Найдено " + QString::number(rows.size()) + " сообщений");
}
void MainWindow::on_search_query_returnPressed()
{
MainWindow::on_search_query_textChanged(ui->search_query->text());
}
std::string MainWindow::get_current_datetime_as_string()
{
char buffer[256];
std::time_t t = std::time(nullptr);
if (std::strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", std::localtime(&t)))
{
return std::string(buffer);
}
return "[undefined]";
}
std::string MainWindow::local_date_time_as_string(const boost::local_time::local_date_time &ldt)
{
char buffer[256];
auto dt_tm = boost::local_time::to_tm(ldt);
if (strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &dt_tm))
{
return std::string(buffer);
}
return "[undefined]";
}
void MainWindow::closeEvent(QCloseEvent *event)
{
std::lock_guard<std::mutex> main_lock(main_mutex);
if (QMessageBox::question(this, "Внимание", "Вы уверены, что хотите выйти?", QMessageBox::Yes | QMessageBox::No,
QMessageBox::No) == QMessageBox::Yes)
{
clear_all();
event->accept();
}
else
{
event->ignore();
}
}
void MainWindow::clear_all()
{
try
{
imap_settings.set_autoupdate(false);
ui->folders_tree->clear();
ui->messages_table->clearContents();
ui->messages_table->setRowCount(0);
ui->current_message->setDocument(&this->empty_text_document);
ui->logs_text_edit->clear();
ui->search_query->clear();
ui->status_bar->clearMessage();
update_status = true;
update_status_message.clear();
folders.clear();
current_item.clear();
messages_sizes.clear();
messages.clear();
messages_ids_to_uids.clear();
messages_ids_to_text_documents.clear();
uids_to_messages_ids.clear();
mailboxes_to_messages_ids.clear();
}
catch (...)
{
}
}
Соседние файлы в папке MailClient