match Session.cpp
[ghsmtp.git] / message-test.cpp
blob1ac5196cbef2dbc09116f0a9b1acba97820a2432
1 #include "message.hpp"
3 #include "Now.hpp"
4 #include "Pill.hpp"
5 #include "Reply.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 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")};
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 std::string("(none)");
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(std::back_inserter(bfr), "Message-ID: {}\r\n",
79 mid_str.c_str());
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);
98 message::parsed msg;
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);
107 if (authentic)
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),
120 server_identity);
122 message::rewrite_from_to(msg, "", reply_to, server_identity.c_str(),
123 selector, key_file);
125 std::cout << msg.as_string();
127 // auth again
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
134 CHECK_EQ(count, 1);
136 else {
137 LOG(INFO) << "message failed to parse";
140 if (FLAGS_arc) {
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;
145 file.open(argv[a]);
146 message::parsed msg;
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();
151 return 0;
154 if (FLAGS_dkim) {
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;
159 file.open(argv[a]);
160 message::parsed msg;
161 CHECK(msg.parse(std::string_view(file.data(), file.size())));
162 message::dkim_check(msg, server_identity.c_str());
164 return 0;
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;
172 file.open(argv[a]);
173 message::parsed msg;
174 CHECK(msg.parse(std::string_view(file.data(), file.size())));
175 message::print_spf_envelope_froms(argv[a], msg);
177 return 0;
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;
184 file.open(argv[a]);
185 message::parsed msg;
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();