-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathMessageStore.cpp
146 lines (125 loc) · 2.94 KB
/
MessageStore.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include "MessageStore.hpp"
#include "osutil.hpp"
#include <cstdlib>
#include <fmt/format.h>
#include <fmt/ostream.h>
namespace {
auto locate_maildir() -> fs::path
{
auto const maildir_ev{getenv("MAILDIR")};
if (maildir_ev) {
return maildir_ev;
}
else {
return osutil::get_home_dir() / "Maildir";
}
}
} // namespace
void MessageStore::open(std::string_view fqdn,
std::streamsize max_size,
std::string_view folder)
{
auto maildir = locate_maildir();
if (!folder.empty()) {
maildir /= folder;
}
newfn_ = maildir / "new";
tmpfn_ = maildir / "tmp";
tmp2fn_ = maildir / "tmp";
error_code ec;
create_directories(newfn_, ec);
create_directories(tmpfn_, ec);
// Unique name, see: <https://cr.yp.to/proto/maildir.html>
auto const uniq{
fmt::format("{}.R{}.{}", then_.sec(), s_.as_string_view(), fqdn)};
newfn_ /= uniq;
tmpfn_ /= uniq;
auto const uniq2{
fmt::format("{}.R{}2.{}", then_.sec(), s_.as_string_view(), fqdn)};
tmp2fn_ /= uniq2;
// open
ofs_.exceptions(std::ifstream::failbit | std::ifstream::badbit);
ofs_.open(tmpfn_);
max_size_ = max_size;
size_ = 0;
}
std::ostream& MessageStore::write(char const* s, std::streamsize count)
{
if (!size_error_ && (size_ + count) <= max_size_) {
size_ += count;
return ofs_.write(s, count);
}
else {
size_error_ = true;
return ofs_;
}
}
void MessageStore::try_close_()
{
try {
if (ofs_.is_open())
ofs_.close();
}
catch (std::system_error const& e) {
LOG(ERROR) << e.what() << "code: " << e.code();
}
catch (std::exception const& e) {
LOG(ERROR) << e.what();
}
}
std::string_view MessageStore::freeze()
{
try_close_();
error_code ec;
if (fs::exists(tmp2fn_)) {
fs::remove(tmp2fn_, ec);
if (ec) {
LOG(WARNING) << "problem removing " << tmp2fn_ << ": " << ec;
}
}
rename(tmpfn_, tmp2fn_, ec);
if (ec) {
LOG(ERROR) << "can't rename " << tmpfn_ << " to " << tmp2fn_ << ": " << ec;
}
ofs_.open(tmpfn_);
mapping_.open(tmp2fn_);
size_ = 0;
return std::string_view(mapping_.data(), mapping_.size());
}
void MessageStore::deliver()
{
if (size_error()) {
LOG(WARNING) << "message size error: " << size() << " exceeds "
<< max_size();
}
try_close_();
error_code ec;
rename(tmpfn_, newfn_, ec);
if (ec) {
LOG(ERROR) << "can't rename " << tmpfn_ << " to " << newfn_ << ": " << ec;
}
if (fs::exists(newfn_)) {
LOG(INFO) << "successfully deliverd " << newfn_;
}
else {
LOG(ERROR) << "failed to deliver " << newfn_;
}
}
void MessageStore::close()
{
try_close_();
error_code ec;
fs::remove(tmpfn_, ec);
if (ec) {
LOG(ERROR) << "can't remove " << tmpfn_ << ": " << ec;
}
if (fs::exists(tmp2fn_)) {
fs::remove(tmp2fn_, ec);
if (ec) {
LOG(ERROR) << "can't remove " << tmp2fn_ << ": " << ec;
}
}
if (mapping_.is_open()) {
mapping_.close();
}
}