5 // #include "Reply.hpp"
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 #include <tao/pegtl.hpp>
21 #include <tao/pegtl/contrib/abnf.hpp>
23 using namespace tao::pegtl
;
24 using namespace tao::pegtl::abnf
;
26 constexpr char srs_secret
[] = "Not a real secret, of course.";
28 DEFINE_bool(arc
, false, "check ARC set");
29 DEFINE_bool(dkim
, false, "check DKIM sigs");
30 DEFINE_bool(print_from
, false, "print envelope froms");
32 DEFINE_string(selector
, "ghsmtp", "DKIM selector");
34 static std::string
make_string(std::string_view v
)
36 return std::string(v
.begin(),
37 static_cast<size_t>(std::distance(v
.begin(), v
.end())));
40 int main(int argc
, char* argv
[])
42 google::ParseCommandLineFlags(&argc
, &argv
, true);
44 auto server_identity
= [] {
45 auto const id_from_env
{getenv("GHSMTP_SERVER_ID")};
47 return std::string
{id_from_env
};
49 auto const hostname
{osutil::get_hostname()};
50 if (hostname
.find('.') != std::string::npos
)
53 LOG(FATAL
) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
54 return std::string("(none)");
57 auto constexpr authentication_results_str
=
58 "Authentication-Results: digilicious.com;\r\n"
59 " spf=pass smtp.helo=mta122b.pmx1.epsl1.com;\r\n"
60 " dkim=pass header.i=@mail.paypal.com header.s=pp-epsilon1 "
61 "header.b=\"A4JA0zWd\";\r\n"
62 " dmarc=fail header.from=mail.paypal.com;\r\n"
65 std::string authservid
;
66 std::string ar_result
;
67 CHECK(message::authentication_results_parse(authentication_results_str
,
68 authservid
, ar_result
));
69 CHECK_EQ(authservid
, "digilicious.com");
71 auto const dom_from
{Domain(server_identity
)};
72 auto const dom_to
{Domain(server_identity
)};
74 auto const config_path
= osutil::get_config_dir();
76 auto const selector
= FLAGS_selector
.c_str();
78 auto const key_file
= (config_path
/ selector
).replace_extension("private");
79 CHECK(fs::exists(key_file
)) << "can't find key file " << key_file
;
81 Mailbox
from("gene", dom_from
);
82 Mailbox
to("anything", dom_to
);
84 auto const date
{Now
{}};
85 auto const pill
{Pill
{}};
86 auto const mid_str
= fmt::format("<{}.{}@{}>", date
.sec(),
87 pill
.as_string_view(), server_identity
);
89 fmt::memory_buffer bfr
;
90 fmt::format_to(std::back_inserter(bfr
), "Message-ID: {}\r\n",
92 fmt::format_to(std::back_inserter(bfr
),
93 "From: \"Gene (more \\) comments) Hightower\" <{}>\r\n",
94 from
.as_string(Mailbox::domain_encoding::utf8
));
95 fmt::format_to(std::back_inserter(bfr
), "To: \"Gene Hightower\" <{}>\r\n",
96 to
.as_string(Mailbox::domain_encoding::utf8
));
97 fmt::format_to(std::back_inserter(bfr
),
98 "Subject: Testing, one, two, three.\r\n");
99 fmt::format_to(std::back_inserter(bfr
), "Date: {}\r\n", date
.c_str());
100 fmt::format_to(std::back_inserter(bfr
),
101 "Authentication-Results: {}; none\r\n", server_identity
);
102 fmt::format_to(std::back_inserter(bfr
), "MIME-Version: 1.0\r\n");
103 fmt::format_to(std::back_inserter(bfr
),
104 "Content-Type: text/plain; charset=utf-8\r\n");
106 fmt::format_to(std::back_inserter(bfr
), "\r\n");
108 fmt::format_to(std::back_inserter(bfr
), "This is the body of the email.\r\n");
109 auto const msg_str
= fmt::to_string(bfr
);
113 bool const message_parsed
= msg
.parse(msg_str
);
115 if (message_parsed
) {
116 LOG(INFO
) << "message parsed";
118 auto const authentic
=
119 authentication(msg
, server_identity
.c_str(), selector
, key_file
);
122 LOG(INFO
) << "authentic";
124 Reply::from_to reply
;
126 reply
.mail_from
= msg
.dmarc_from
;
127 reply
.rcpt_to_local_part
= "local-alias";
129 auto const rfc22_from
= fmt::format(
130 "From: {}@{}", Reply::enc_reply(reply
, srs_secret
), server_identity
);
132 auto const reply_to
=
133 fmt::format("Reply-To: {}@{}", Reply::enc_reply(reply
, srs_secret
),
136 message::rewrite_from_to(msg
, "", reply_to
, server_identity
.c_str(),
139 std::cout
<< msg
.as_string();
142 // authentication(msg, server_identity.c_str(), selector, key_file);
144 auto const count
= std::count_if(
145 begin(msg
.headers
), end(msg
.headers
),
146 [](auto const& hdr
) { return hdr
== message::Authentication_Results
; });
147 // should have only one header, no matter what
151 LOG(INFO
) << "message failed to parse";
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::authentication(msg
, server_identity
.c_str(), selector
, key_file
);
163 std::cout
<< msg
.as_string();
169 for (int a
= 1; a
< argc
; ++a
) {
170 if (!fs::exists(argv
[a
]))
171 LOG(FATAL
) << "can't find mail file " << argv
[a
];
172 boost::iostreams::mapped_file_source file
;
175 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
176 message::dkim_check(msg
, server_identity
.c_str());
181 if (FLAGS_print_from
) {
182 for (int a
= 1; a
< argc
; ++a
) {
183 if (!fs::exists(argv
[a
]))
184 LOG(FATAL
) << "can't find mail file " << argv
[a
];
185 boost::iostreams::mapped_file_source file
;
188 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
189 message::print_spf_envelope_froms(argv
[a
], msg
);
195 for (int a
= 1; a
< argc
; ++a
) {
196 if (!fs::exists(argv
[a
]))
197 LOG(FATAL
) << "can't find mail file " << argv
[a
];
198 boost::iostreams::mapped_file_source file
;
201 CHECK(msg
.parse(std::string_view(file
.data(), file
.size())));
203 message::mailbox_name_addr_list from_parsed
;
204 // Should be only one From:
206 std::find(begin(msg
.headers
), end(msg
.headers
), message::From
);
207 hdr
!= end(msg
.headers
)) {
208 auto const from_str
= make_string(hdr
->value
);
210 LOG(INFO
) << "about to parse From:" << from_str
;
211 if (message::mailbox_list_parse(from_str
, from_parsed
)) {
212 LOG(INFO
) << "success parsing From:" << from_str
;
215 LOG(WARNING
) << "failed to parse From:" << from_str
;
218 for (auto hdr_next
= std::next(hdr
); hdr_next
!= end(msg
.headers
);
219 hdr_next
= std::next(hdr_next
)) {
220 if (*hdr_next
== message::From
) {
221 LOG(WARNING
) << "additional RFC5322.From header found:«"
222 << hdr_next
->as_string() << "»";
227 if (from_parsed
.name_addr_list
.empty()) {
228 LOG(WARNING
) << "No address in RFC5322.From header";
232 for (auto const& na
: from_parsed
.name_addr_list
) {
233 auto from_name
= na
.name
;
234 LOG(INFO
) << "from_name «" << from_name
<< "»";
236 auto from_addr
= na
.addr
;
237 LOG(INFO
) << "from_addr «" << from_addr
<< "»";
239 if (!Mailbox::validate(from_addr
)) {
240 LOG(WARNING
) << "Mailbox syntax valid for RFC-5322, not for RFC-5321: «"
245 // std::cout << msg.as_string();