Добавил:
СПбГУТ * ИКСС * Программная инженерия Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

8 семестр / Готовая курсовая работа / ММиВА. Курсовая работа

.pdf
Скачиваний:
31
Добавлен:
09.03.2022
Размер:
2.04 Mб
Скачать

12#include <QTextDocumentFragment>

13#include <QThread>

14#include <QTreeWidget>

15

16#include "imap_settings.h"

17#include "smtp_settings.h"

19QT_BEGIN_NAMESPACE

20namespace Ui

21{

22class MainWindow;

23}

24QT_END_NAMESPACE

26class ThreadForManualUpdate; // declaration for thread_for_manual_update attribute in MainWindow class

27class ThreadForAutoUpdate; // declaration for thread_for_update attribute in MainWindow class

28

29class MainWindow : public QMainWindow

30{

31Q_OBJECT

32

33public:

34MainWindow(QWidget *parent = nullptr);

35~MainWindow() override;

36

37private slots:

38void update_folders_and_messages_ui();

39void on_update_folders_and_messages_triggered();

40void on_set_up_account_triggered();

41void on_exit_program_triggered();

42void on_add_folder_triggered();

43void on_rename_folder_triggered();

44void on_delete_selected_folder_triggered();

45void on_send_message_triggered();

46void on_download_attachment_triggered();

47void on_copy_selected_message_triggered();

48void on_delete_selected_message_triggered();

49void on_clear_all_and_disable_autoupdate_triggered();

50void on_show_message_statistics_triggered();

51void on_show_about_window_triggered();

52void on_folders_tree_itemClicked(QTreeWidgetItem *, int);

53void on_messages_table_itemClicked(QTableWidgetItem *);

54void on_search_query_textChanged(const QString &);

55void on_search_query_returnPressed();

56

57private:

58void update_folders_and_messages();

59void folder_chosen_ui();

60static int get_number_of_folders_recursive(QTreeWidgetItem *twi);

61static void list_folders_recursive(

62QTreeWidgetItem *twi, const mailio::imaps::mailbox_folder_t &folder, mailio::imaps &conn,

63const std::list<std::string> &path, std::unordered_map<std::string, mailio::message> &messages,

64std::unordered_map<std::string, unsigned long long> &messages_sizes,

65std::map<std::list<std::string>, std::unordered_set<std::string>> &mailboxes_to_messages_ids,

66std::unordered_map<std::string, unsigned long> &messages_ids_to_uids,

67std::unordered_map<unsigned long, std::string> &uids_to_messages_ids);

68static std::string get_current_datetime_as_string();

69static std::string local_date_time_as_string(const

boost::local_time::local_date_time &ldt);

70void closeEvent(QCloseEvent *event) override;

71void clear_all();

72

73Ui::MainWindow *ui;

74QTextDocument empty_text_document;

75ImapSettings imap_settings;

76SmtpSettings smtp_settings;

51

77ThreadForManualUpdate *thread_for_manual_update;

78ThreadForAutoUpdate *thread_for_auto_update;

79std::mutex main_mutex;

80QList<QTreeWidgetItem *> folders;

81std::list<std::string> current_item;

82std::unordered_map<std::string, unsigned long long> messages_sizes;

83std::unordered_map<std::string, mailio::message> messages;

84std::unordered_map<std::string, unsigned long> messages_ids_to_uids;

85std::unordered_map<std::string, QTextDocument> messages_ids_to_text_documents;

86std::unordered_map<unsigned long, std::string> uids_to_messages_ids;

87std::map<std::list<std::string>, std::unordered_set<std::string>>

mailboxes_to_messages_ids;

88std::chrono::system_clock::time_point start;

89const std::chrono::duration<long long int> timeout = std::chrono::seconds(5000);

90bool update_status;

91std::string update_status_message;

92friend class ThreadForManualUpdate;

93friend class ThreadForAutoUpdate;

94};

95

96class ThreadForManualUpdate : public QThread

97{

98Q_OBJECT

99

100public:

101explicit ThreadForManualUpdate(MainWindow *mainwindow) : mainwindow(mainwindow)

102{

103if (!mainwindow)

104{

105

throw std::runtime_error("mainwindow is null");

106}

107}

108~ThreadForManualUpdate()

109{

110}

111

112void run()

113{

114std::lock_guard<std::mutex> main_lock(mainwindow->main_mutex);

115mainwindow->update_folders_and_messages();

116emit folders_and_messages_updated();

117}

118

119private:

120MainWindow *mainwindow;

121signals:

122void folders_and_messages_updated();

123};

124

125class ThreadForAutoUpdate : public QThread

126{

127Q_OBJECT

128

129public:

130explicit ThreadForAutoUpdate(MainWindow *mainwindow, const ImapSettings *imap_settings)

131: mainwindow(mainwindow), imap_settings(imap_settings)

132{

133if (!mainwindow)

134{

135

throw std::runtime_error("mainwindow is null");

136}

137if (!imap_settings)

138{

139

throw std::runtime_error("imap_settings is null");

140}

141}

142~ThreadForAutoUpdate()

143{

144}

145

52

146void run()

147{

148this->current_second = 1;

149while (true)

150{

151

if (imap_settings->get_autoupdate())

152

{

153

if (this->current_second < imap_settings->get_autoupdate_interval())

154

{

155

this->current_second += 1;

156

}

157

else

158

{

159

this->current_second = 1;

160

std::lock_guard<std::mutex> main_lock(mainwindow->main_mutex);

161

mainwindow->update_folders_and_messages();

162

emit folders_and_messages_updated();

163

}

164

}

165

else

166

{

167

this->current_second = 1;

168

}

169

QThread::sleep(1);

170}

171}

173private:

174MainWindow *mainwindow;

175const ImapSettings *imap_settings;

176int current_second;

177signals:

178void folders_and_messages_updated();

179};

180

181 #endif // MAINWINDOW_H

Модуль «mainwindow.cpp»

1#include <QInputDialog>

2#include <QMessageBox>

4#include "mainwindow.h"

5#include "send_message_dialog.h"

6#include "settings_dialog.h"

7#include "tools.h"

8#include "ui_mainwindow.h"

10MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)

11{

12ui->setupUi(this);

13ui->folders_tree->header()- >setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);

14ui->folders_tree->setSortingEnabled(true);

15ui->folders_tree->sortByColumn(0, Qt::AscendingOrder);

16ui->messages_table->horizontalHeader()- >resizeSections(QHeaderView::ResizeMode::ResizeToContents);

17ui->messages_table->setSortingEnabled(true);

18ui->messages_table->sortByColumn(2, Qt::DescendingOrder);

19// imap initial settings

20imap_settings.set_server_address("imap.yandex.ru");

21imap_settings.set_server_port(993);

22imap_settings.set_login("robert.wayne.alx@yandex.ru");

23imap_settings.set_password("Robw12345");

24imap_settings.set_autoupdate(false);

25imap_settings.set_autoupdate_interval(10);

26imap_settings.set_auth_method(mailio::imaps::auth_method_t::LOGIN);

27// smtp initial settings

28smtp_settings.set_server_address("smtp.yandex.ru");

53

29smtp_settings.set_server_port(465);

30smtp_settings.set_login("robert.wayne.alx@yandex.ru");

31smtp_settings.set_password("Robw12345");

32smtp_settings.set_name("robert.wayne.alx");

33smtp_settings.set_auth_method(mailio::smtps::auth_method_t::LOGIN);

34// other

35ui->messages_table->hideColumn(ui->messages_table->columnCount() - 1);

36ui->folders_tree->hideColumn(ui->folders_tree->columnCount() - 1);

37ui->horizontal_splitter->setStretchFactor(0, 0);

38ui->horizontal_splitter->setStretchFactor(1, 1);

39this->update_status = true;

40// threads for update folders and messages

41thread_for_auto_update = new ThreadForAutoUpdate(this, &imap_settings);

42connect(thread_for_auto_update, SIGNAL(folders_and_messages_updated()), this,

43

SLOT(update_folders_and_messages_ui()));

44thread_for_manual_update = new ThreadForManualUpdate(this);

45connect(thread_for_manual_update, SIGNAL(folders_and_messages_updated()), this,

46

SLOT(update_folders_and_messages_ui()));

47thread_for_auto_update->start();

48}

49

50MainWindow::~MainWindow()

51{

52delete ui;

53}

54

55void MainWindow::update_folders_and_messages()

56{

57try

58{

59this->start = std::chrono::system_clock::now();

60mailio::imaps conn = mailio::imaps(imap_settings.get_server_address(),

static_cast<unsigned>(imap_settings.get_server_port()), timeout);

62conn.authenticate(imap_settings.get_login(), imap_settings.get_password(), imap_settings.get_auth_method());

63mailio::imaps::mailbox_folder_t mailbox_main_folder = conn.list_folders("");

64std::unordered_map<std::string, unsigned long> new_messages_ids_to_uids;

65std::unordered_map<unsigned long, std::string> new_uids_to_messages_ids;

66std::map<std::list<std::string>, std::unordered_set<std::string>>

new_mailboxes_to_messages_ids;

67QTreeWidgetItem root;

68list_folders_recursive(&root, mailbox_main_folder, conn, std::list<std::string>(), this->messages,

69

this->messages_sizes, new_mailboxes_to_messages_ids,

 

new_messages_ids_to_uids,

70

new_uids_to_messages_ids);

71QList<QTreeWidgetItem *> new_folders;

72for (size_t i = 0, n = root.childCount(); i < n; ++i)

73{

74

new_folders.emplace_back(root.child(i)->clone());

75}

76this->messages_ids_to_uids = new_messages_ids_to_uids;

77this->uids_to_messages_ids = new_uids_to_messages_ids;

78this->mailboxes_to_messages_ids = new_mailboxes_to_messages_ids;

79this->folders.swap(new_folders);

80this->update_status = true;

81this->update_status_message.clear();

82}

83catch (const std::exception &exc)

84{

85this->update_status = false;

86this->update_status_message = "Список папок с сообщениями не обновлен: " + std::string(exc.what());

87}

88}

89

90void MainWindow::list_folders_recursive(

91QTreeWidgetItem *twi, const mailio::imaps::mailbox_folder_t &folder, mailio::imaps &conn,

54

92const std::list<std::string> &path, std::unordered_map<std::string, mailio::message> &messages,

93std::unordered_map<std::string, unsigned long long> &messages_sizes,

94std::map<std::list<std::string>, std::unordered_set<std::string>> &mailboxes_to_messages_ids,

95std::unordered_map<std::string, unsigned long> &messages_ids_to_uids,

96std::unordered_map<unsigned long, std::string> &uids_to_messages_ids)

97{

98for (auto &folder : folder.folders)

99{

100try

101{

102

std::list<std::string> new_list(path);

103

new_list.emplace_back(folder.first);

104

mailboxes_to_messages_ids[new_list];

105

QTreeWidgetItem *child = new QTreeWidgetItem();

106

twi->addChild(child);

107

child->setText(0, utf7decode(QString::fromStdString(folder.first)));

108

child->setText(3, QString::fromStdString(folder.first));

109

if (!folder.second.folders.empty())

110

{

111

list_folders_recursive(child, folder.second, conn, new_list, messages,

 

messages_sizes,

112

mailboxes_to_messages_ids,

 

messages_ids_to_uids, uids_to_messages_ids);

113

}

114

mailio::imaps::mailbox_stat_t stat;

115

try

116

{

117

stat = conn.select(new_list, true);

118

}

119

catch (...)

120

{

121

stat.messages_unseen = 0;

122

stat.messages_no = 0;

123

}

124

child->setText(1, QString::number(stat.messages_unseen));

125

child->setText(2, QString::number(stat.messages_no));

126

std::list<mailio::imaps::messages_range_t> ranges;

127

ranges.emplace_back(mailio::imaps::messages_range_t(1,

 

std::optional<unsigned long>(-1)));

128

std::map<unsigned long, mailio::message> found_messages;

129

try

130

{

131

conn.fetch(ranges, found_messages, true, true,

 

mailio::codec::line_len_policy_t::VERYLARGE);

132

}

133

catch (...)

134

{

135

child->setText(1, "-");

136

child->setText(2, "-");

137

continue;

138

}

139

if (found_messages.empty())

140

{

141

child->setText(1, "0");

142

child->setText(2, "0");

143

continue;

144

}

145

for (auto message_it = found_messages.begin(); message_it !=

 

found_messages.end(); ++message_it)

146

{

147

try

148

{

149

unsigned long uid = message_it->first;

150

mailio::message current_message = message_it->second;

151

std::string message_id = current_message.message_id();

152

messages_ids_to_uids[message_id] = uid;

153

uids_to_messages_ids[uid] = message_id;

154

mailboxes_to_messages_ids[new_list].insert(message_id);

155

if (messages.find(message_id) == messages.end())

55

156

{

157

// update if empty

158

messages[message_id] = current_message;

159

std::string message_source;

160

current_message.format(message_source);

161

messages_sizes[message_id] = message_source.size();

162

// try to get full message (may except)

163

try

164

{

165

conn.fetch(uid, current_message, true, false);

166

current_message.format(message_source, false);

167

QString qmessage_source =

 

QString::fromStdString(message_source);

168

qmessage_source.replace("This is a MIME-encapsulated

 

message.", "");

169

message_source = qmessage_source.toStdString();

170

messages[message_id].parse(message_source, false);

171

messages_sizes[message_id] = message_source.size();

172

if (current_message.parts().empty())

173

{

174

std::string::size_type pos =

 

message_source.find("\r\n\r\n");

175

if (pos == std::string::npos)

176

{

177

continue;

178

}

179

std::string content = message_source.substr(pos + 4,

 

std::string::npos);

180

messages[message_id].content(content);

181

}

182

}

183

catch (...)

184

{

185

// try another way

186

std::string new_message_id(message_id);

187

while (new_message_id.size() > 2)

188

{

189

new_message_id.assign(new_message_id.begin() + 1,

 

new_message_id.end() - 1);

190

if (messages.find(new_message_id) != messages.end())

191

{

192

messages[new_message_id].format(message_source,

 

false);

193

messages[message_id].parse(message_source, false);

194

messages_sizes[message_id] =

 

message_source.size();

195

break;

196

}

197

}

198

}

199

}

200

}

201

catch (...)

202

{

203

}

204

}

205}

206catch (...)

207{

208}

209}

210}

211

212void MainWindow::update_folders_and_messages_ui()

213{

214if (!this->update_status)

215{

216std::string current_datetime_s = get_current_datetime_as_string();

217QString log_message = QString::fromStdString(current_datetime_s + ". " + this- >update_status_message);

218ui->status_bar->showMessage(log_message);

56

219ui->logs_text_edit->appendPlainText(log_message);

220return;

221}

222try

223{

224std::string update_message;

225bool ok = false;

226try

227{

228

ui->folders_tree->clear();

229

ui->folders_tree->addTopLevelItems(this->folders);

230

ui->folders_tree->expandAll();

231

int width = 0;

232

for (int i = 0, n = ui->folders_tree->columnCount(); i < n; ++i)

233

{

234

width += ui->folders_tree->columnWidth(i);

235

}

236

ui->horizontal_splitter->setSizes(QList<int>{width + 10, ui-

 

>centralwidget->width() - width - 10});

237

std::chrono::duration<double> sec = std::chrono::system_clock::now() -

 

this->start;

238

update_message = "Список папок с сообщениями обновлен, сообщения с

 

вложениями доступны оффлайн (" +

239

std::to_string(sec.count()) + " сек.)";

240

ok = true;

241}

242catch (const std::exception &exc)

243{

244

update_message = "Список папок с сообщениями не обновлен: " +

 

std::string(exc.what());

245}

246std::string current_datetime_s = get_current_datetime_as_string();

247QString log_message = QString::fromStdString(current_datetime_s + ". " + update_message);

248ui->status_bar->showMessage(log_message);

249ui->logs_text_edit->appendPlainText(log_message);

250if (ok)

251{

252

folder_chosen_ui();

253}

254}

255catch (const std::exception &exc)

256{

257QString log_message =

258

QString::fromStdString("Ошибка обновления списка папок с сообщениями: " +

 

std::string(exc.what()));

259ui->status_bar->showMessage(log_message);

260ui->logs_text_edit->appendPlainText(log_message);

261}

262}

263

264void MainWindow::folder_chosen_ui()

265{

266try

267{

268QTreeWidgetItem *current_folder = ui->folders_tree->currentItem();

269std::list<std::string> folder_name;

270if (current_folder == nullptr)

271{

272

if (this->current_item.empty())

273

{

274

return;

275

}

276

folder_name = this->current_item;

277}

278else

279{

280

QTreeWidgetItem *current = current_folder;

281

while (current)

282

{

283

folder_name.emplace_front(current->text(3).toStdString());

57

284

current = current->parent();

285

}

286

this->current_item = folder_name;

287}

288unsigned long index = 0, n_messages = 0;

289std::list<std::tuple<unsigned long, unsigned long, QTableWidgetItem *>> cells;

290std::unordered_set<std::string> current_messages = this-

>mailboxes_to_messages_ids[folder_name];

291constexpr unsigned long long kb = 1 << 10, mb = kb << 10, gb = mb << 10;

292for (auto it = current_messages.begin(); it != current_messages.end(); ++it,

++index)

293{

294

try

295

{

296

unsigned long uid = this->messages_ids_to_uids[*it];

297

mailio::message current_message = this->messages[*it];

298

std::string message_id = current_message.message_id();

299

QTableWidgetItem *r0item, *r1item, *r2item, *r3item, *r4item, *r5item,

 

*r6item, *r7item;

300

try

301

{

302

r0item = new

 

QTableWidgetItem(QString::fromStdString(current_message.subject()).simplified());

303

}

304

catch (...)

305

{

306

r0item = new QTableWidgetItem("[undefined]");

307

}

308

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 0, r0item));

309

++n_messages;

310

try

311

{

312

r1item =

313

new

 

QTableWidgetItem(QString::fromStdString(current_message.from_to_string()).simplified()

 

);

314

}

315

catch (...)

316

{

317

r1item = new QTableWidgetItem("[undefined]");

318

}

319

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 1, r1item));

320

try

321

{

322

r2item = new QTableWidgetItem(

323

 

 

QString::fromStdString(local_date_time_as_string(current_message.date_time())));

324

}

325

catch (...)

326

{

327

r2item = new QTableWidgetItem("[undefined]");

328

}

329

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 2, r2item));

330

try

331

{

332

r3item = new QTableWidgetItem(

333

 

 

QString::fromStdString(current_message.recipients_to_string()).simplified());

334

}

335

catch (...)

336

{

337

r3item = new QTableWidgetItem("[undefined]");

338

}

339

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 3, r3item));

340

try

341

{

58

342

 

r4item = new

 

QTableWidgetItem(QString::number(current_message.attachments_size()));

343

}

 

344

catch (...)

345

{

 

346

 

r4item = new QTableWidgetItem("[undefined]");

347

}

 

348

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 4, r4item));

349

try

 

350

{

 

351

 

unsigned long long size = this->messages_sizes[message_id];

352

 

std::string size_s;

353

 

if (size >= gb)

354

 

{

355

 

size = size * 100 / gb;

356

 

size_s = std::to_string(size / 100) + "." +

 

std::to_string(size

% 100) + " ГиБ";

357

 

}

358

 

else if (size >= mb)

359

 

{

360

 

size = size * 100 / mb;

361

 

size_s = std::to_string(size / 100) + "." +

 

std::to_string(size

% 100) + " МиБ";

362

 

}

363

 

else if (size >= kb)

364

 

{

365

 

size = size * 100 / kb;

366

 

size_s = std::to_string(size / 100) + "." +

 

std::to_string(size

% 100) + " КиБ";

367

 

}

368

 

else

369

 

{

370

 

size_s = std::to_string(size) + " Б";

371

 

}

372

 

r5item = new QTableWidgetItem(QString::fromStdString(size_s));

373

}

 

374

catch (...)

375

{

 

376

 

r5item = new QTableWidgetItem("[undefined]");

377

}

 

378

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 5, r5item));

379

try

 

380

{

 

381

 

r6item = new QTableWidgetItem(QString::number(uid));

382

}

 

383

catch (...)

384

{

 

385

 

r6item = new QTableWidgetItem("[undefined]");

386

}

 

387

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 6, r6item));

388

r7item = new QTableWidgetItem(QString::fromStdString(*it));

389

cells.emplace_back(std::tuple<unsigned long, unsigned long,

 

QTableWidgetItem *>(index, 7, r7item));

390

if (this->messages_ids_to_text_documents.find(message_id) == this-

 

>messages_ids_to_text_documents.end())

391

{

 

392

 

QString all_parts;

393

 

std::vector<mailio::mime> mimes = current_message.parts();

394

 

if (mimes.empty())

395

 

{

396

 

all_parts +=

 

QString::fromStdString(current_message.content());

397

 

}

398

 

for (const mailio::mime &mime : mimes)

399

 

{

400

 

mailio::mime::content_disposition_t

 

content_disposition(mime.content_disposition());

59

402

 

403

mailio::message::content_type_t

 

content_type(mime.content_type());

404

if (content_type.type == mailio::mime::media_type_t::TEXT

 

&& content_type.subtype == "html")

405

{

406

all_parts += QString::fromStdString(mime.content());

407

}

408

else

409

{

410

all_parts +=

411

 

 

QTextDocumentFragment::fromPlainText(QString::fromStdString(mime.content()))

412

.toHtml();

413

}

414

}

415

}

416

this-

 

>messages_ids_to_text_documents[message_id].setHtml(all_parts);

417

}

418

}

419

catch (const std::exception &exc)

420

{

421

ui->logs_text_edit->appendPlainText(

422

QString::fromStdString("Ошибка чтения сообщения " + *it + ": " +

 

std::string(exc.what())));

423

}

424

}

425

ui->messages_table->setSortingEnabled(false);

426

ui->messages_table->setRowCount(0);

427

ui->messages_table->setRowCount(n_messages);

428

unsigned long row, col;

429

QTableWidgetItem *item;

430

for (auto cell : cells)

431

{

432

std::tie(row, col, item) = cell;

433

ui->messages_table->setItem(row, col, item);

434

}

435

ui->messages_table->horizontalHeader()-

 

>resizeSections(QHeaderView::ResizeMode::ResizeToContents);

436

ui->messages_table->setSortingEnabled(true);

437

}

438

catch (const std::exception &exc)

439

{

440

QString log_message =

441

QString::fromStdString("Ошибка чтения сообщений выбранной папки: " +

401

std::string(exc.what()));

if (content_disposition !=

 

mailio::mime::content_disposition_t::ATTACHMENT)

{

442ui->status_bar->showMessage(log_message);

443ui->logs_text_edit->appendPlainText(log_message);

444}

445}

446

447void MainWindow::on_update_folders_and_messages_triggered()

448{

449if (!thread_for_manual_update->isRunning())

450{

451thread_for_manual_update->start();

452}

453}

454

455void MainWindow::on_set_up_account_triggered()

456{

457std::lock_guard<std::mutex> main_lock(main_mutex);

458try

459{

460SettingsDialog new_settings_dialog;

461new_settings_dialog.set_imap_settings(imap_settings);

462new_settings_dialog.set_smtp_settings(smtp_settings);

463if (new_settings_dialog.exec() == QDialog::Accepted)

60

Соседние файлы в папке Готовая курсовая работа