try to fix git tangle
[ghsmtp.git] / Mailbox-test.cpp
blob8b5aaff7b9301183b2c86e93d1766404d6ea99f0
1 #include "Mailbox.hpp"
3 #include <iostream>
5 #include <glog/logging.h>
7 #include <fmt/format.h>
8 #include <fmt/ostream.h>
10 template <>
11 struct fmt::formatter<Mailbox> : ostream_formatter {};
13 using namespace std::string_literals;
15 int main(int argc, char* argv[])
17 CHECK_EQ(Mailbox{"\"a.b\"@foo.bar"}, Mailbox{"a.b@foo.bar"});
18 CHECK_EQ(Mailbox{"\"a..b\"@foo.bar"}, Mailbox{"\"\\a\\.\\.\\b\"@foo.bar"});
20 CHECK_EQ(Mailbox{"pOsTmAsTeR"}, Mailbox{"postmaster"});
22 Mailbox mb;
23 CHECK(mb.empty());
25 Mailbox dg0{"gene@digilicious.com"};
26 Mailbox dg1{"gene", Domain{"digilicious.com"}};
28 CHECK_EQ(dg0, dg1);
30 CHECK_EQ(std::string("digilicious.com"), dg0.domain().ascii());
32 auto dgstr = static_cast<std::string>(dg0);
34 CHECK_EQ(dgstr, "gene@digilicious.com");
36 dg0.clear();
37 CHECK(dg0.empty());
39 auto threw = false;
40 try {
41 Mailbox bad("should throw@example.com");
43 catch (std::exception& e) {
44 threw = true;
46 CHECK(threw);
48 std::string msg;
49 Mailbox mbx;
50 CHECK(Mailbox::validate("simple@example.com", msg, mbx));
51 CHECK(Mailbox::validate("very.common@example.com", msg, mbx));
52 CHECK(Mailbox::validate("disposable.style.email.with+symbol@example.com", msg,
53 mbx));
54 CHECK(Mailbox::validate("other.email-with-hyphen@example.com", msg, mbx));
55 CHECK(Mailbox::validate("fully-qualified-domain@example.com", msg, mbx));
57 // (may go to user.name@example.com inbox depending on mail server)
58 CHECK(Mailbox::validate("user.name+tag+sorting@example.com", msg, mbx));
60 CHECK(Mailbox::validate("x@example.com", msg, mbx));
61 CHECK(Mailbox::validate("example-indeed@strange-example.com", msg, mbx));
63 CHECK(Mailbox::validate("example@s.example", msg, mbx));
65 // (space between the quotes)
66 CHECK(Mailbox::validate("\" \"@example.org", msg, mbx));
68 // (quoted angle brackets)
69 CHECK(Mailbox::validate("\"\\<foo-bar\\>\"@example.org", msg, mbx));
71 // (quoted double dot)
72 CHECK(Mailbox::validate("\"john..doe\"@example.org", msg, mbx));
74 // (bangified host route used for uucp mailers)
75 CHECK(Mailbox::validate("mailhost!username@example.org", msg, mbx));
77 // (% escaped mail route to user@example.com via example.org)
78 CHECK(Mailbox::validate("user%example.com@example.org", msg, mbx));
80 // Invalid email addresses
82 CHECK(!Mailbox::validate("Abc.example.com", msg, mbx)); // (no @ character)
83 CHECK_EQ(msg, "invalid mailbox syntax «Abc.example.com»"s);
85 CHECK(!Mailbox::validate("A@b@c@example.com", msg, mbx)); // (only one @ is
86 // allowed)
87 CHECK_EQ(msg, "invalid mailbox syntax «A@b@c@example.com»"s);
89 // (none of the special characters in this local-part are allowed
90 // outside quotation marks)
91 CHECK(!Mailbox::validate("a\"b(c)d,e:f;g<h>i[j\\k]l@example.com", msg, mbx));
92 CHECK_EQ(msg,
93 "invalid mailbox syntax «a\"b(c)d,e:f;g<h>i[j\\k]l@example.com»"s);
95 // (quoted strings must be dot separated or the only element making
96 // up the local-part)
97 CHECK(!Mailbox::validate("just\"not\"right@example.com", msg, mbx));
98 CHECK_EQ(msg, "invalid mailbox syntax «just\"not\"right@example.com»"s);
100 // (spaces, quotes, and backslashes may only exist when within
101 // quoted strings and preceded by a backslash)
102 CHECK(!Mailbox::validate("this is\"not\\allowed@example.com", msg, mbx));
103 CHECK_EQ(msg, "invalid mailbox syntax «this is\"not\\allowed@example.com»"s);
105 // (even if escaped (preceded by a backslash), spaces, quotes, and
106 // backslashes must still be contained by quotes)
107 CHECK(!Mailbox::validate("this\\ still\\\"not\\\\allowed@example.com", msg,
108 mbx));
109 CHECK_EQ(
110 msg,
111 "invalid mailbox syntax «this\\ still\\\"not\\\\allowed@example.com»"s);
113 // (local part is longer than 64 characters)
114 // Real world local-parts often longer than "offical" limit.
115 // CHECK(!Mailbox::validate(
116 // "1234567890123456789012345678901234567890123456789012345"
117 // "678901234+x@example.com"));
119 // Not fully qualified
120 Mailbox foobar{"foo@bar"};
121 CHECK(Mailbox::validate(foobar.as_string(Mailbox::domain_encoding::ascii),
122 msg, mbx));
123 CHECK(!domain::is_fully_qualified(foobar.domain(), msg));
124 CHECK_EQ(msg, "domain «bar» must have two or more labels"s);
126 // TLD too short, but okay at this level.
127 Mailbox foobarx{"foo@bar.x"};
128 CHECK(Mailbox::validate(foobarx.as_string(Mailbox::domain_encoding::ascii),
129 msg, mbx));
130 CHECK(!domain::is_fully_qualified(foobarx.domain(), msg));
131 CHECK_EQ(msg, "TLD «x» must be two or more octets"s);
133 // Label longer than 64 octets.
134 CHECK(!Mailbox::validate(
135 "foo@12345678901234567890123456789012345678901234567890123456789012345."
136 "com",
137 msg, mbx));
138 CHECK_EQ(
139 msg,
140 "domain label «12345678901234567890123456789012345678901234567890123456789012345» too long"s);
142 // Total domain length too long.
143 CHECK(!Mailbox::validate(
144 "foo@"
145 "123456789012345678901234567890123456789012345678901234567890123."
146 "123456789012345678901234567890123456789012345678901234567890123."
147 "123456789012345678901234567890123456789012345678901234567890123."
148 "123456789012345678901234567890123456789012345678901234567890123."
149 "com",
150 msg, mbx));
151 CHECK_EQ(msg,
152 "domain name «"
153 "123456789012345678901234567890123456789012345678901234567890123."
154 "123456789012345678901234567890123456789012345678901234567890123."
155 "123456789012345678901234567890123456789012345678901234567890123."
156 "123456789012345678901234567890123456789012345678901234567890123."
157 "com» too long"s);
159 CHECK(Mailbox::validate("gene@digilicious.com", msg, mbx));
160 CHECK(Mailbox::validate("gene@[127.0.0.1]", msg, mbx));
161 CHECK(!Mailbox::validate("gene@[127.999.0.1]", msg, mbx));
162 CHECK_EQ(msg, "invalid mailbox syntax «gene@[127.999.0.1]»"s);
163 CHECK(!Mailbox::validate("allen@bad_d0main.com", msg, mbx));
164 CHECK_EQ(msg, "invalid mailbox syntax «allen@bad_d0main.com»"s);
167 auto const res = Mailbox::parse("gene@[127.0.0.1]");
168 CHECK(res && res->local_type == Mailbox::local_types::dot_string);
169 CHECK(res && res->domain_type == Mailbox::domain_types::address_literal);
172 auto const res = Mailbox::parse("\"some string\"@example.com");
173 CHECK(res && res->local_type == Mailbox::local_types::quoted_string);
174 CHECK(res && res->domain_type == Mailbox::domain_types::domain);
177 CHECK(!Mailbox::validate("2962", msg, mbx));
178 CHECK_EQ(msg, "invalid mailbox syntax «2962»"s);
180 CHECK(Mailbox::validate("실례@실례.테스트", msg, mbx));
182 // <https://docs.microsoft.com/en-us/archive/blogs/testing123/email-address-test-cases>
184 // Valid email addresses:
185 CHECK(Mailbox::validate("email@domain.com", msg, mbx));
187 // Email contains dot in the local part, a dot-atom-string.
188 CHECK(Mailbox::validate("firstname.lastname@domain.com", msg, mbx));
190 // Multiple labels in domain.
191 CHECK(Mailbox::validate("email@subdomain.domain.com", msg, mbx));
193 // Plus sign is a valid character.
194 CHECK(Mailbox::validate("firstname+lastname@domain.com", msg, mbx));
196 // Domain is valid IP address, but this is matched as a domain.
197 auto const raw_ips = "email@123.123.123.123";
198 CHECK(Mailbox::validate(raw_ips, msg, mbx));
200 // Square bracket around IP address is a "address literal."
201 auto const add_lit = "email@[123.123.123.123]";
202 CHECK(Mailbox::validate(add_lit, msg, mbx));
204 CHECK_EQ(Mailbox{raw_ips}, Mailbox{add_lit});
206 // Quotes around local part is valid.
207 CHECK(Mailbox::validate("\"email\"@domain.com", msg, mbx));
209 // But same mailbox.
210 CHECK_EQ(Mailbox{"\"email\"@domain.com"}, Mailbox{"email@domain.com"});
212 // Digits in address are valid.
213 CHECK(Mailbox::validate("1234567890@domain.com", msg, mbx));
215 // Dash in domain name is valid.
216 CHECK(Mailbox::validate("email@domain-one.com", msg, mbx));
218 // Underscore in the address field is valid.
219 CHECK(Mailbox::validate("_______@domain.com", msg, mbx));
221 CHECK(Mailbox::validate("email@domain.name", msg, mbx));
222 CHECK(Mailbox::validate("email@domain.co.jp", msg, mbx));
224 // Dash in local part is valid.
225 CHECK(Mailbox::validate("firstname-lastname@domain.com", msg, mbx));
227 CHECK(!Mailbox::validate("plainaddress", msg, mbx)); // Missing @ sign and
228 // domain
229 CHECK(!Mailbox::validate("#@%^%#$@#$@#.com", msg, mbx)); // Garbage
230 CHECK(!Mailbox::validate("@domain.com", msg, mbx)); // Missing username
232 CHECK(!Mailbox::validate("Joe Smith <email@domain.com>", msg, mbx));
234 CHECK(!Mailbox::validate("email.domain.com", msg, mbx)); // Missing @
235 CHECK(!Mailbox::validate("email@domain@domain.com", msg, mbx)); // Two @ sign
237 // Leading dot in address is not allowed
238 CHECK(!Mailbox::validate(".email@domain.com", msg, mbx));
240 // Trailing dot in address is not allowed
241 CHECK(!Mailbox::validate("email.@domain.com", msg, mbx));
243 // Multiple dots
244 CHECK(!Mailbox::validate("email..email@domain.com", msg, mbx));
246 // OK! Unicode char as address
247 CHECK(Mailbox::validate("あいうえお@domain.com", msg, mbx));
249 // Comment not allowed in 5321 mailbox.
250 CHECK(!Mailbox::validate("email@domain.com (Joe Smith)", msg, mbx));
252 // Missing top level domain (.com/.net/.org/etc).
253 CHECK(Mailbox::validate("email@domain", msg, mbx)) << msg;
255 // Leading dash in front of domain is invalid.
256 CHECK(!Mailbox::validate("email@-domain.com", msg, mbx));
258 // .web is not a valid top level domain, oh yeah? says who?
259 CHECK(Mailbox::validate("email@domain.web", msg, mbx));
261 // Invalid IP address.
262 CHECK(!Mailbox::validate("email@[111.222.333.44444]", msg, mbx));
264 // Invalid IP address, but valid domain name as it turns out.
265 CHECK(Mailbox::validate("email@111.222.333.44444", msg, mbx));
267 // Not a valid domain name.
268 CHECK(!Mailbox::validate("email@domain..com", msg, mbx));
270 // general_address_literal
271 CHECK(!Mailbox::validate("email@[x:~Foo_Bar_Baz<\?\?>]", msg, mbx));
273 std::cout << "sizeof(Mailbox) == " << sizeof(Mailbox) << '\n';