3 #include "OpenDKIM.hpp"
4 #include "OpenDMARC.hpp"
8 #include "imemstream.hpp"
15 #include <fmt/format.h>
16 #include <fmt/ostream.h>
18 #include <boost/algorithm/string.hpp>
19 #include <boost/iostreams/device/mapped_file.hpp>
21 using namespace std::string_literals
;
23 DEFINE_string(signer
, "digilicious.com", "signing domain");
24 DEFINE_string(selector
, "ghsmtp", "DKIM selector");
26 bool arc_sign(message::parsed
& msg
,
30 char const* server_id
)
32 CHECK(!msg
.headers
.empty());
37 for (auto const& header
: msg
.headers
) {
38 arv
.header(header
.as_view());
44 LOG(INFO
) << "ARC status == " << arv
.chain_status_str();
45 LOG(INFO
) << "ARC custody == " << arv
.chain_custody_str();
47 auto const arc_status
= arv
.chain_status_str();
49 // Run our message through ARC::sign
53 if (iequal(arc_status
, "none")) {
56 else if (iequal(arc_status
, "fail")) {
59 else if (iequal(arc_status
, "pass")) {
66 for (auto const& header
: msg
.headers
) {
67 ars
.header(header
.as_view());
73 boost::iostreams::mapped_file_source priv
;
76 std::string ar_results
= "None";
77 for (auto hdr
: msg
.headers
) {
78 if (hdr
== message::Authentication_Results
) {
79 std::string authservid
;
80 if (message::authentication_results_parse(hdr
.as_view(), authservid
,
82 if (Domain::match(authservid
, sender
))
84 LOG(INFO
) << "ignoring AR: " << hdr
.as_string();
86 LOG(WARNING
) << "failed to parse «"
87 << esc(hdr
.as_string(), esc_line_option::multi
) << "»";
92 if (ars
.seal(sender
, selector
, sender
, priv
.data(), priv
.size(),
93 ar_results
.c_str())) {
94 msg
.arc_hdrs
= ars
.whole_seal();
95 for (auto const& hdr
: msg
.arc_hdrs
) {
96 CHECK(msg
.parse_hdr(hdr
));
100 LOG(INFO
) << "failed to generate seal";
103 OpenARC::verify arv2
;
104 for (auto const& header
: msg
.headers
) {
105 arv2
.header(header
.as_view());
111 LOG(INFO
) << "check ARC status == " << arv2
.chain_status_str();
112 LOG(INFO
) << "check ARC custody == " << arv2
.chain_custody_str();
114 return "fail"s
!= arv2
.chain_status_str();
117 int main(int argc
, char* argv
[])
119 google::ParseCommandLineFlags(&argc
, &argv
, true);
121 auto const server_identity
= [] {
122 auto const id_from_env
{getenv("GHSMTP_SERVER_ID")};
124 return std::string
{id_from_env
};
126 auto const hostname
{osutil::get_hostname()};
127 if (hostname
.find('.') != std::string::npos
)
130 LOG(FATAL
) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
134 auto const config_path
= osutil::get_config_dir();
136 auto const selector
= FLAGS_selector
.c_str();
138 auto const key_file
= (config_path
/ selector
).replace_extension("private");
139 CHECK(fs::exists(key_file
)) << "can't find key file " << key_file
;
141 auto const dom_signer
{Domain(FLAGS_signer
)};
143 for (int a
= 1; a
< argc
; ++a
) {
144 if (!fs::exists(argv
[a
]))
145 LOG(FATAL
) << "can't find mail file " << argv
[a
];
146 boost::iostreams::mapped_file_source file
;
149 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
150 CHECK(arc_sign(msg
, dom_signer
.ascii().c_str(), selector
, key_file
,
151 server_identity
.c_str()));
152 std::cout
<< msg
.as_string();