don't try to write data after error
[ghsmtp.git] / MessageStore.cpp
blobb873eb4608fc055bfe029aec01db1ca827d5f0c8
1 #include "MessageStore.hpp"
3 #include "osutil.hpp"
5 #include <cstdlib>
7 #include <fmt/format.h>
8 #include <fmt/ostream.h>
10 namespace {
11 auto locate_maildir() -> fs::path
13 auto const maildir_ev{getenv("MAILDIR")};
14 if (maildir_ev) {
15 return maildir_ev;
17 else {
18 return osutil::get_home_dir() / "Maildir";
21 } // namespace
23 void MessageStore::open(std::string_view fqdn,
24 std::streamsize max_size,
25 std::string_view folder)
27 auto maildir = locate_maildir();
29 if (!folder.empty()) {
30 maildir /= folder;
33 newfn_ = maildir / "new";
34 tmpfn_ = maildir / "tmp";
35 tmp2fn_ = maildir / "tmp";
37 error_code ec;
38 create_directories(newfn_, ec);
39 create_directories(tmpfn_, ec);
41 // Unique name, see: <https://cr.yp.to/proto/maildir.html>
42 auto const uniq{
43 fmt::format("{}.R{}.{}", then_.sec(), s_.as_string_view(), fqdn)};
44 newfn_ /= uniq;
45 tmpfn_ /= uniq;
47 auto const uniq2{
48 fmt::format("{}.R{}2.{}", then_.sec(), s_.as_string_view(), fqdn)};
49 tmp2fn_ /= uniq2;
51 // open
52 ofs_.exceptions(std::ifstream::failbit | std::ifstream::badbit);
53 ofs_.open(tmpfn_);
55 max_size_ = max_size;
56 size_ = 0;
59 std::ostream& MessageStore::write(char const* s, std::streamsize count)
61 if (!size_error_ && (size_ + count) <= max_size_) {
62 size_ += count;
63 return ofs_.write(s, count);
65 else {
66 size_error_ = true;
67 return ofs_;
71 void MessageStore::try_close_()
73 try {
74 if (ofs_.is_open())
75 ofs_.close();
77 catch (std::system_error const& e) {
78 LOG(ERROR) << e.what() << "code: " << e.code();
80 catch (std::exception const& e) {
81 LOG(ERROR) << e.what();
85 std::string_view MessageStore::freeze()
87 try_close_();
88 error_code ec;
89 if (fs::exists(tmp2fn_)) {
90 fs::remove(tmp2fn_, ec);
91 if (ec) {
92 LOG(WARNING) << "problem removing " << tmp2fn_ << ": " << ec;
95 rename(tmpfn_, tmp2fn_, ec);
96 if (ec) {
97 LOG(ERROR) << "can't rename " << tmpfn_ << " to " << tmp2fn_ << ": " << ec;
99 ofs_.open(tmpfn_);
100 mapping_.open(tmp2fn_);
101 size_ = 0;
102 return std::string_view(mapping_.data(), mapping_.size());
105 void MessageStore::deliver()
107 if (size_error()) {
108 LOG(WARNING) << "message size error: " << size() << " exceeds "
109 << max_size();
112 try_close_();
114 error_code ec;
115 rename(tmpfn_, newfn_, ec);
116 if (ec) {
117 LOG(ERROR) << "can't rename " << tmpfn_ << " to " << newfn_ << ": " << ec;
120 if (fs::exists(newfn_)) {
121 LOG(INFO) << "successfully deliverd " << newfn_;
123 else {
124 LOG(ERROR) << "failed to deliver " << newfn_;
128 void MessageStore::close()
130 try_close_();
132 error_code ec;
133 fs::remove(tmpfn_, ec);
134 if (ec) {
135 LOG(ERROR) << "can't remove " << tmpfn_ << ": " << ec;
137 if (fs::exists(tmp2fn_)) {
138 fs::remove(tmp2fn_, ec);
139 if (ec) {
140 LOG(ERROR) << "can't remove " << tmp2fn_ << ": " << ec;
143 if (mapping_.is_open()) {
144 mapping_.close();