check for non-ASCII local-parts without SMTPUTF8
[ghsmtp.git] / message-test.cpp
blob316ba061b9b1f0b9b156fef5ec6054f3c291dc17
1 #include "message.hpp"
3 #include "Now.hpp"
4 #include "Pill.hpp"
5 #include "SRS0.hpp"
6 #include "esc.hpp"
7 #include "osutil.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>
18 #include <iostream>
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")};
34 if (id_from_env)
35 return std::string{id_from_env};
37 auto const hostname{osutil::get_hostname()};
38 if (hostname.find('.') != std::string::npos)
39 return hostname;
41 LOG(FATAL) << "can't determine my server ID, set GHSMTP_SERVER_ID maybe";
42 return "(none)"s;
43 }();
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"
51 " arc=none";
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{}};
74 auto const mid_str =
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);
94 message::parsed msg;
95 bool const message_parsed = msg.parse(msg_str);
97 if (message_parsed) {
98 LOG(INFO) << "message parsed";
100 auto const authentic =
101 authentication(msg, server_identity.c_str(), selector, key_file);
103 if (authentic)
104 LOG(INFO) << "authentic";
106 SRS0 srs(config_path);
108 SRS0::from_to reply;
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(),
120 selector, key_file);
122 std::cout << msg.as_string();
124 // auth again
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
131 CHECK_EQ(count, 1);
133 else {
134 LOG(INFO) << "message failed to parse";
137 if (FLAGS_arc) {
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;
142 file.open(argv[a]);
143 message::parsed msg;
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();
148 return 0;
151 if (FLAGS_dkim) {
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;
156 file.open(argv[a]);
157 message::parsed msg;
158 CHECK(msg.parse(std::string_view(file.data(), file.size())));
159 message::dkim_check(msg, server_identity.c_str());
161 return 0;
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;
169 file.open(argv[a]);
170 message::parsed msg;
171 CHECK(msg.parse(std::string_view(file.data(), file.size())));
172 message::print_spf_envelope_froms(argv[a], msg);
174 return 0;
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;
181 file.open(argv[a]);
182 message::parsed msg;
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();