9 #include <fmt/format.h>
10 #include <fmt/ostream.h>
12 #include <gflags/gflags.h>
14 #include <glog/logging.h>
16 #include <boost/iostreams/device/mapped_file.hpp>
20 using namespace std::string_literals
;
22 DEFINE_bool(arc
, false, "check ARC set");
23 DEFINE_bool(dkim
, false, "check DKIM sigs");
24 DEFINE_bool(print_from
, false, "print envelope froms");
26 DEFINE_string(selector
, "ghsmtp", "DKIM selector");
28 int main(int argc
, char* argv
[])
30 google::ParseCommandLineFlags(&argc
, &argv
, true);
32 auto server_identity
= [] {
33 auto const id_from_env
{getenv("GHSMTP_SERVER_ID")};
35 return std::string
{id_from_env
};
37 auto const hostname
{osutil::get_hostname()};
38 if (hostname
.find('.') != std::string::npos
)
41 LOG(FATAL
) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
45 auto constexpr authentication_results_str
=
46 "Authentication-Results: digilicious.com;\r\n"
47 " spf=pass smtp.helo=mta122b.pmx1.epsl1.com;\r\n"
48 " dkim=pass header.i=@mail.paypal.com header.s=pp-epsilon1 "
49 "header.b=\"A4JA0zWd\";\r\n"
50 " dmarc=fail header.from=mail.paypal.com;\r\n"
53 std::string authservid
;
54 std::string ar_result
;
55 CHECK(message::authentication_results_parse(authentication_results_str
,
56 authservid
, ar_result
));
57 CHECK_EQ(authservid
, "digilicious.com");
59 auto const dom_from
{Domain(server_identity
)};
60 auto const dom_to
{Domain(server_identity
)};
62 auto const config_path
= osutil::get_config_dir();
64 auto const selector
= FLAGS_selector
.c_str();
66 auto const key_file
= (config_path
/ selector
).replace_extension("private");
67 CHECK(fs::exists(key_file
)) << "can't find key file " << key_file
;
69 Mailbox
from("gene", dom_from
);
70 Mailbox
to("anything", dom_to
);
72 auto const date
{Now
{}};
73 auto const pill
{Pill
{}};
75 fmt::format("<{}.{}@{}>", date
.sec(), pill
, server_identity
);
77 fmt::memory_buffer bfr
;
78 fmt::format_to(bfr
, "Message-ID: {}\r\n", mid_str
.c_str());
79 fmt::format_to(bfr
, "From: \"Gene Hightower\" <{}>\r\n",
80 from
.as_string(Mailbox::domain_encoding::utf8
));
81 fmt::format_to(bfr
, "To: \"Gene Hightower\" <{}>\r\n",
82 to
.as_string(Mailbox::domain_encoding::utf8
));
83 fmt::format_to(bfr
, "Subject: Testing, one, two, three.\r\n");
84 fmt::format_to(bfr
, "Date: {}\r\n", date
.c_str());
85 fmt::format_to(bfr
, "Authentication-Results: {}; none\r\n", server_identity
);
86 fmt::format_to(bfr
, "MIME-Version: 1.0\r\n");
87 fmt::format_to(bfr
, "Content-Type: text/plain; charset=utf-8\r\n");
89 fmt::format_to(bfr
, "\r\n");
91 fmt::format_to(bfr
, "This is the body of the email.\r\n");
92 auto const msg_str
= fmt::to_string(bfr
);
95 bool const message_parsed
= msg
.parse(msg_str
);
98 LOG(INFO
) << "message parsed";
100 auto const authentic
=
101 authentication(msg
, server_identity
.c_str(), selector
, key_file
);
104 LOG(INFO
) << "authentic";
106 SRS0
srs(config_path
);
110 reply
.mail_from
= msg
.dmarc_from
;
111 reply
.rcpt_to_local_part
= "local-alias";
113 auto const rfc22_from
=
114 fmt::format("From: {}@{}", srs
.enc_reply(reply
), server_identity
);
116 auto const reply_to
=
117 fmt::format("Reply-To: {}@{}", srs
.enc_reply(reply
), server_identity
);
119 message::rewrite_from_to(msg
, "", reply_to
, server_identity
.c_str(),
122 std::cout
<< msg
.as_string();
125 // authentication(msg, server_identity.c_str(), selector, key_file);
127 auto const count
= std::count_if(
128 begin(msg
.headers
), end(msg
.headers
),
129 [](auto const& hdr
) { return hdr
== message::Authentication_Results
; });
130 // should have only one header, no matter what
134 LOG(INFO
) << "message failed to parse";
138 for (int a
= 1; a
< argc
; ++a
) {
139 if (!fs::exists(argv
[a
]))
140 LOG(FATAL
) << "can't find mail file " << argv
[a
];
141 boost::iostreams::mapped_file_source file
;
144 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
145 message::authentication(msg
, server_identity
.c_str(), selector
, key_file
);
146 std::cout
<< msg
.as_string();
152 for (int a
= 1; a
< argc
; ++a
) {
153 if (!fs::exists(argv
[a
]))
154 LOG(FATAL
) << "can't find mail file " << argv
[a
];
155 boost::iostreams::mapped_file_source file
;
158 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
159 message::dkim_check(msg
, server_identity
.c_str());
164 if (FLAGS_print_from
) {
165 for (int a
= 1; a
< argc
; ++a
) {
166 if (!fs::exists(argv
[a
]))
167 LOG(FATAL
) << "can't find mail file " << argv
[a
];
168 boost::iostreams::mapped_file_source file
;
171 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
172 message::print_spf_envelope_froms(argv
[a
], msg
);
177 for (int a
= 1; a
< argc
; ++a
) {
178 if (!fs::exists(argv
[a
]))
179 LOG(FATAL
) << "can't find mail file " << argv
[a
];
180 boost::iostreams::mapped_file_source file
;
183 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
184 rewrite_from_to(msg
, "bounce@digilicious.com", "noreply@digilicious.com",
185 server_identity
.c_str(), selector
, key_file
);
186 std::cout
<< msg
.as_string();