Merge pull request #210 from jengelh/twoaddr
Handle parsing of further non-conformant From lines
This commit is contained in:
commit
414661858d
@ -80,189 +80,73 @@ void mailbox::parseImpl(
|
|||||||
const char* const pstart = buffer.data() + position;
|
const char* const pstart = buffer.data() + position;
|
||||||
const char* p = pstart;
|
const char* p = pstart;
|
||||||
|
|
||||||
// Ignore blank spaces at the beginning
|
|
||||||
while (p < pend && parserHelpers::isSpace(*p)) ++p;
|
|
||||||
|
|
||||||
// Current state for parsing machine
|
// Current state for parsing machine
|
||||||
enum States {
|
enum States {
|
||||||
State_None,
|
State_None,
|
||||||
|
State_String,
|
||||||
State_Name,
|
State_Name,
|
||||||
State_Address
|
State_Bracket,
|
||||||
|
State_Address_Bracketed,
|
||||||
|
State_Address_Unbracketed,
|
||||||
|
State_NameAt,
|
||||||
};
|
};
|
||||||
|
|
||||||
States state = State_Name; // let's start with name, we will see later (*)
|
States state = State_None;
|
||||||
|
|
||||||
// Temporary buffers for extracted name and address
|
// Temporary buffers for extracted name and address
|
||||||
string name;
|
string name, address;
|
||||||
string address;
|
unsigned int hold = 0;
|
||||||
bool hadBrackets = false;
|
bool hadBrackets = false;
|
||||||
|
|
||||||
while (p < pend) {
|
while (p < pend) {
|
||||||
|
if (state == State_None) {
|
||||||
if (state == State_Name) {
|
while (p < pend && parserHelpers::isSpace(*p))
|
||||||
|
++p;
|
||||||
|
if (*p == ',' || *p == ';')
|
||||||
|
/* Completely new mailbox -- stop parsing this one. */
|
||||||
|
break;
|
||||||
if (*p == '<') {
|
if (*p == '<') {
|
||||||
|
state = State_Bracket;
|
||||||
state = State_Address;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (*p == '"') { // Quoted string
|
if (*p == '"') { // Quoted string
|
||||||
|
state = State_String;
|
||||||
++p;
|
++p;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
state = State_Name;
|
||||||
|
} else if (state == State_String) {
|
||||||
bool escaped = false;
|
bool escaped = false;
|
||||||
|
|
||||||
while (p < pend) {
|
for (; p < pend; ++p) {
|
||||||
|
|
||||||
if (escaped) {
|
if (escaped) {
|
||||||
|
|
||||||
name += *p;
|
name += *p;
|
||||||
escaped = false;
|
escaped = false;
|
||||||
|
continue;
|
||||||
} else if (*p == '\\') {
|
} else if (*p == '\\') {
|
||||||
|
|
||||||
escaped = true;
|
escaped = true;
|
||||||
|
continue;
|
||||||
} else {
|
}
|
||||||
|
|
||||||
if (*p == '"') {
|
if (*p == '"') {
|
||||||
|
|
||||||
++p;
|
++p;
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
name += *p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
bool escaped = false;
|
|
||||||
int comment = 0;
|
|
||||||
|
|
||||||
while (p < pend) {
|
|
||||||
|
|
||||||
if (escaped) {
|
|
||||||
|
|
||||||
if (!comment) name += *p;
|
|
||||||
escaped = false;
|
|
||||||
|
|
||||||
} else if (comment) {
|
|
||||||
|
|
||||||
if (*p == '\\') {
|
|
||||||
escaped = true;
|
|
||||||
} else if (*p == '(') {
|
|
||||||
++comment;
|
|
||||||
} else if (*p == ')') {
|
|
||||||
--comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (*p == '\\') {
|
|
||||||
|
|
||||||
escaped = true;
|
|
||||||
|
|
||||||
} else if (*p == '(') {
|
|
||||||
|
|
||||||
++comment;
|
|
||||||
|
|
||||||
} else if (*p == '<') {
|
|
||||||
|
|
||||||
// Erase any space between display name and <address>
|
|
||||||
string::iterator q = name.end();
|
|
||||||
|
|
||||||
while (q != name.begin() && parserHelpers::isSpace(*(q - 1))) {
|
|
||||||
--q;
|
|
||||||
}
|
|
||||||
|
|
||||||
name.erase(q, name.end());
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else if (/* parserHelpers::isSpace(*p) || */ *p == '@') {
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
name += *p;
|
|
||||||
}
|
|
||||||
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p < pend && *p == '@') {
|
|
||||||
|
|
||||||
// (*) Actually, we were parsing the local-part of an address
|
|
||||||
// and not a display name...
|
|
||||||
address = name;
|
|
||||||
name.clear();
|
|
||||||
|
|
||||||
bool escaped = false;
|
|
||||||
int comment = 0;
|
|
||||||
|
|
||||||
while (p < pend) {
|
|
||||||
|
|
||||||
if (escaped) {
|
|
||||||
|
|
||||||
if (!comment) address += *p;
|
|
||||||
escaped = false;
|
|
||||||
|
|
||||||
} else if (comment) {
|
|
||||||
|
|
||||||
if (*p == '\\') {
|
|
||||||
escaped = true;
|
|
||||||
} else if (*p == '(') {
|
|
||||||
++comment;
|
|
||||||
} else if (*p == ')') {
|
|
||||||
--comment;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if (*p == '\\') {
|
|
||||||
|
|
||||||
escaped = true;
|
|
||||||
|
|
||||||
} else if (*p == '(') {
|
|
||||||
|
|
||||||
++comment;
|
|
||||||
|
|
||||||
} else if (parserHelpers::isSpace(*p)) {
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
address += *p;
|
|
||||||
}
|
|
||||||
|
|
||||||
++p;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
while (p < pend && parserHelpers::isSpace(*p)) ++p;
|
|
||||||
state = State_None;
|
state = State_None;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
name += *p;
|
||||||
} else if (state == State_Address) {
|
}
|
||||||
|
} else if (state == State_Address_Bracketed) {
|
||||||
bool escaped = false;
|
bool escaped = false;
|
||||||
int comment = 0;
|
int comment = 0;
|
||||||
|
|
||||||
while (p < pend) {
|
for (; p < pend; ++p) {
|
||||||
|
|
||||||
if (escaped) {
|
if (escaped) {
|
||||||
|
if (!comment) {
|
||||||
if (!comment) address += *p;
|
address += *p;
|
||||||
|
name += *p;
|
||||||
|
++hold;
|
||||||
|
}
|
||||||
escaped = false;
|
escaped = false;
|
||||||
|
|
||||||
} else if (comment) {
|
} else if (comment) {
|
||||||
|
|
||||||
if (*p == '\\') {
|
if (*p == '\\') {
|
||||||
escaped = true;
|
escaped = true;
|
||||||
} else if (*p == '(') {
|
} else if (*p == '(') {
|
||||||
@ -270,48 +154,151 @@ void mailbox::parseImpl(
|
|||||||
} else if (*p == ')') {
|
} else if (*p == ')') {
|
||||||
--comment;
|
--comment;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (*p == '(') {
|
} else if (*p == '(') {
|
||||||
|
|
||||||
++comment;
|
++comment;
|
||||||
|
|
||||||
} else if (*p == '\\') {
|
} else if (*p == '\\') {
|
||||||
|
|
||||||
escaped = true;
|
escaped = true;
|
||||||
|
|
||||||
} else if (*p == '<') {
|
} else if (*p == '<') {
|
||||||
|
state = State_Bracket;
|
||||||
|
break;
|
||||||
|
} else if (*p == '>') {
|
||||||
|
hadBrackets = true;
|
||||||
|
name += *p++;
|
||||||
|
++hold;
|
||||||
|
state = State_Name;
|
||||||
|
break;
|
||||||
|
} else if (parserHelpers::isSpace(*p)) {
|
||||||
|
name += *p;
|
||||||
|
++hold;
|
||||||
|
} else {
|
||||||
|
address += *p;
|
||||||
|
name += *p;
|
||||||
|
++hold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state == State_Address_Unbracketed) {
|
||||||
|
bool escaped = false;
|
||||||
|
int comment = 0;
|
||||||
|
|
||||||
|
for (; p < pend; ++p) {
|
||||||
|
if (escaped) {
|
||||||
|
if (!comment) {
|
||||||
|
address += *p;
|
||||||
|
name += *p;
|
||||||
|
++hold;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
} else if (comment) {
|
||||||
|
if (*p == '\\')
|
||||||
|
escaped = true;
|
||||||
|
else if (*p == '(')
|
||||||
|
++comment;
|
||||||
|
else if (*p == ')')
|
||||||
|
--comment;
|
||||||
|
} else if (*p == '(') {
|
||||||
|
++comment;
|
||||||
|
} else if (*p == '\\') {
|
||||||
|
escaped = true;
|
||||||
|
} else if (*p == '<') {
|
||||||
|
state = State_Bracket;
|
||||||
|
break;
|
||||||
|
} else if (*p == '>') {
|
||||||
|
name += *p++;
|
||||||
|
++hold;
|
||||||
|
state = State_None;
|
||||||
|
break;
|
||||||
|
} else if (*p == ',' || *p == ';') {
|
||||||
|
state = State_None;
|
||||||
|
break;
|
||||||
|
} else if (parserHelpers::isSpace(*p)) {
|
||||||
|
state = State_Name;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
address += *p;
|
||||||
|
name += *p;
|
||||||
|
++hold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state == State_Name) {
|
||||||
|
bool escaped = false;
|
||||||
|
unsigned int comment = 0, at_hold = 0;
|
||||||
|
|
||||||
|
for (; p < pend; ++p) {
|
||||||
|
if (escaped) {
|
||||||
|
if (!comment) {
|
||||||
|
name += *p;
|
||||||
|
++at_hold;
|
||||||
|
}
|
||||||
|
escaped = false;
|
||||||
|
} else if (comment) {
|
||||||
|
if (*p == '\\')
|
||||||
|
escaped = true;
|
||||||
|
else if (*p == '(')
|
||||||
|
++comment;
|
||||||
|
else if (*p == ')')
|
||||||
|
--comment;
|
||||||
|
} else if (*p == '\\') {
|
||||||
|
escaped = true;
|
||||||
|
} else if (*p == '(') {
|
||||||
|
++comment;
|
||||||
|
} else if (*p == '<') {
|
||||||
|
state = State_Bracket;
|
||||||
|
break;
|
||||||
|
} else if (*p == '@') {
|
||||||
|
hold = at_hold;
|
||||||
|
state = State_NameAt;
|
||||||
|
break;
|
||||||
|
} else if (parserHelpers::isSpace(*p)) {
|
||||||
|
name += *p;
|
||||||
|
++hold;
|
||||||
|
at_hold = 1;
|
||||||
|
} else {
|
||||||
|
name += *p;
|
||||||
|
hold = 0;
|
||||||
|
++at_hold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state == State_Bracket) {
|
||||||
|
string::const_iterator q = name.cend();
|
||||||
|
unsigned int nh = 0;
|
||||||
|
while (nh < hold && q != name.cbegin() && parserHelpers::isSpace(*(q - 1))) {
|
||||||
|
--q;
|
||||||
|
++nh;
|
||||||
|
}
|
||||||
|
hold = nh;
|
||||||
|
name += *p++;
|
||||||
|
++hold;
|
||||||
|
if (!address.empty())
|
||||||
// If we found a '<' here, it means that the address
|
// If we found a '<' here, it means that the address
|
||||||
// starts _only_ here...and the stuff we have parsed
|
// starts _only_ here...and the stuff we have parsed
|
||||||
// before belongs actually to the display name!
|
// before belongs actually to the display name!
|
||||||
name += address;
|
|
||||||
address.clear();
|
address.clear();
|
||||||
|
state = State_Address_Bracketed;
|
||||||
} else if (*p == '>') {
|
} else if (state == State_NameAt) {
|
||||||
|
name += *p++;
|
||||||
hadBrackets = true;
|
++hold;
|
||||||
break;
|
// (*) Actually, we were parsing the local-part of an address
|
||||||
|
// and not a display name...
|
||||||
} else if (!parserHelpers::isSpace(*p)) {
|
// Back up to the last whitespace and treat as address
|
||||||
|
string::const_iterator q = name.cend();
|
||||||
address += *p;
|
unsigned int nh = 0;
|
||||||
|
while (nh < hold && q != name.cbegin() && !parserHelpers::isSpace(*(q - 1))) {
|
||||||
|
--q;
|
||||||
|
++nh;
|
||||||
}
|
}
|
||||||
|
address.assign(q, name.cend());
|
||||||
++p;
|
while (nh < hold && q != name.cbegin() && parserHelpers::isSpace(*(q - 1))) {
|
||||||
}
|
--q;
|
||||||
|
++nh;
|
||||||
break;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
while (p < pend && parserHelpers::isSpace(*p)) ++p;
|
|
||||||
|
|
||||||
if (p < pend) {
|
|
||||||
state = State_Address;
|
|
||||||
}
|
}
|
||||||
|
hold = nh;
|
||||||
|
state = State_Address_Unbracketed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hold <= name.size())
|
||||||
|
name.erase(name.size() - hold);
|
||||||
|
|
||||||
// Swap name and address when no address was found
|
// Swap name and address when no address was found
|
||||||
// (email address is mandatory, whereas name is optional).
|
// (email address is mandatory, whereas name is optional).
|
||||||
if (address.empty() && !name.empty() && !hadBrackets) {
|
if (address.empty() && !name.empty() && !hadBrackets) {
|
||||||
|
@ -30,7 +30,7 @@ VMIME_TEST_SUITE_BEGIN(mailboxTest)
|
|||||||
VMIME_TEST(testParse)
|
VMIME_TEST(testParse)
|
||||||
VMIME_TEST(testEmptyEmailAddress)
|
VMIME_TEST(testEmptyEmailAddress)
|
||||||
VMIME_TEST(testSeparatorInComment)
|
VMIME_TEST(testSeparatorInComment)
|
||||||
VMIME_TEST(testAddressInName)
|
VMIME_TEST(testMalformations)
|
||||||
VMIME_TEST_LIST_END
|
VMIME_TEST_LIST_END
|
||||||
|
|
||||||
|
|
||||||
@ -146,13 +146,28 @@ VMIME_TEST_SUITE_BEGIN(mailboxTest)
|
|||||||
VASSERT_EQ("email2", "bbb@vmime.org", mbox2->getEmail());
|
VASSERT_EQ("email2", "bbb@vmime.org", mbox2->getEmail());
|
||||||
}
|
}
|
||||||
|
|
||||||
void testAddressInName() {
|
void testMalformations() {
|
||||||
|
|
||||||
vmime::mailbox mbox;
|
vmime::mailbox mbox;
|
||||||
mbox.parse("a@b.c <e@f.g>");
|
|
||||||
|
|
||||||
|
mbox.parse("a@b.c <e@f.g>");
|
||||||
VASSERT_EQ("name", vmime::text("a@b.c"), mbox.getName());
|
VASSERT_EQ("name", vmime::text("a@b.c"), mbox.getName());
|
||||||
VASSERT_EQ("email", "e@f.g", mbox.getEmail());
|
VASSERT_EQ("email", "e@f.g", mbox.getEmail());
|
||||||
|
|
||||||
|
mbox.parse("a@b.c e@f.g <h@i.j>");
|
||||||
|
VASSERT_EQ("name", vmime::text("a@b.c e@f.g"), mbox.getName());
|
||||||
|
VASSERT_EQ("email", "h@i.j", mbox.getEmail());
|
||||||
|
|
||||||
|
mbox.parse("Foo <bar<baz@quux.com>");
|
||||||
|
VASSERT_EQ("name", vmime::text("Foo <bar"), mbox.getName());
|
||||||
|
VASSERT_EQ("email", "baz@quux.com", mbox.getEmail());
|
||||||
|
|
||||||
|
mbox.parse("Foo <foo@x.com> <bar@x.com>");
|
||||||
|
VASSERT_EQ("name", vmime::text("Foo <foo@x.com>"), mbox.getName());
|
||||||
|
VASSERT_EQ("email", "bar@x.com", mbox.getEmail());
|
||||||
|
|
||||||
|
mbox.parse("Foo <foo@x.com> Bar <bar@y.com>");
|
||||||
|
VASSERT_EQ("name", vmime::text("Foo <foo@x.com> Bar"), mbox.getName());
|
||||||
|
VASSERT_EQ("email", "bar@y.com", mbox.getEmail());
|
||||||
}
|
}
|
||||||
|
|
||||||
VMIME_TEST_SUITE_END
|
VMIME_TEST_SUITE_END
|
||||||
|
Loading…
Reference in New Issue
Block a user