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 constexpr char srs_secret
[] = "Not a real secret, of course.";
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";
42 return std::string("(none)");
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(std::back_inserter(bfr
), "Message-ID: {}\r\n",
80 fmt::format_to(std::back_inserter(bfr
), "From: \"Gene Hightower\" <{}>\r\n",
81 from
.as_string(Mailbox::domain_encoding::utf8
));
82 fmt::format_to(std::back_inserter(bfr
), "To: \"Gene Hightower\" <{}>\r\n",
83 to
.as_string(Mailbox::domain_encoding::utf8
));
84 fmt::format_to(std::back_inserter(bfr
),
85 "Subject: Testing, one, two, three.\r\n");
86 fmt::format_to(std::back_inserter(bfr
), "Date: {}\r\n", date
.c_str());
87 fmt::format_to(std::back_inserter(bfr
),
88 "Authentication-Results: {}; none\r\n", server_identity
);
89 fmt::format_to(std::back_inserter(bfr
), "MIME-Version: 1.0\r\n");
90 fmt::format_to(std::back_inserter(bfr
),
91 "Content-Type: text/plain; charset=utf-8\r\n");
93 fmt::format_to(std::back_inserter(bfr
), "\r\n");
95 fmt::format_to(std::back_inserter(bfr
), "This is the body of the email.\r\n");
96 auto const msg_str
= fmt::to_string(bfr
);
99 bool const message_parsed
= msg
.parse(msg_str
);
101 if (message_parsed
) {
102 LOG(INFO
) << "message parsed";
104 auto const authentic
=
105 authentication(msg
, server_identity
.c_str(), selector
, key_file
);
108 LOG(INFO
) << "authentic";
110 Reply::from_to reply
;
112 reply
.mail_from
= msg
.dmarc_from
;
113 reply
.rcpt_to_local_part
= "local-alias";
115 auto const rfc22_from
= fmt::format(
116 "From: {}@{}", Reply::enc_reply(reply
, srs_secret
), server_identity
);
118 auto const reply_to
=
119 fmt::format("Reply-To: {}@{}", Reply::enc_reply(reply
, srs_secret
),
122 message::rewrite_from_to(msg
, "", reply_to
, server_identity
.c_str(),
125 std::cout
<< msg
.as_string();
128 // authentication(msg, server_identity.c_str(), selector, key_file);
130 auto const count
= std::count_if(
131 begin(msg
.headers
), end(msg
.headers
),
132 [](auto const& hdr
) { return hdr
== message::Authentication_Results
; });
133 // should have only one header, no matter what
137 LOG(INFO
) << "message failed to parse";
141 for (int a
= 1; a
< argc
; ++a
) {
142 if (!fs::exists(argv
[a
]))
143 LOG(FATAL
) << "can't find mail file " << argv
[a
];
144 boost::iostreams::mapped_file_source file
;
147 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
148 message::authentication(msg
, server_identity
.c_str(), selector
, key_file
);
149 std::cout
<< msg
.as_string();
155 for (int a
= 1; a
< argc
; ++a
) {
156 if (!fs::exists(argv
[a
]))
157 LOG(FATAL
) << "can't find mail file " << argv
[a
];
158 boost::iostreams::mapped_file_source file
;
161 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
162 message::dkim_check(msg
, server_identity
.c_str());
167 if (FLAGS_print_from
) {
168 for (int a
= 1; a
< argc
; ++a
) {
169 if (!fs::exists(argv
[a
]))
170 LOG(FATAL
) << "can't find mail file " << argv
[a
];
171 boost::iostreams::mapped_file_source file
;
174 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
175 message::print_spf_envelope_froms(argv
[a
], msg
);
180 for (int a
= 1; a
< argc
; ++a
) {
181 if (!fs::exists(argv
[a
]))
182 LOG(FATAL
) << "can't find mail file " << argv
[a
];
183 boost::iostreams::mapped_file_source file
;
186 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
187 rewrite_from_to(msg
, "bounce@digilicious.com", "noreply@digilicious.com",
188 server_identity
.c_str(), selector
, key_file
);
189 std::cout
<< msg
.as_string();