Bug 884332 - Part 2: Limit domain labels to 63 chars in HTMLInputElement::IsValidEmailAddress. r=mounir
authorBirunthan Mohanathas <birunthan@mohanathas.com>
Fri, 30 Aug 2013 08:13:35 -0400
changeset 153092 675c4547ff7db94eaeba647da9eb757e81bd123c
parent 153091 26b6835a6dd39291f4c296ec477bf5e42a7871cb
child 153093 63994ce9f006ab7054882132ce5ea7a2a8139bad
push id4254
push userakeybl@mozilla.com
push dateTue, 17 Sep 2013 14:18:33 +0000
treeherdermozilla-aurora@9edd56e694b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmounir
bugs884332
milestone26.0a1
Bug 884332 - Part 2: Limit domain labels to 63 chars in HTMLInputElement::IsValidEmailAddress. r=mounir
content/html/content/src/HTMLInputElement.cpp
content/html/content/test/forms/test_input_email.html
--- a/content/html/content/src/HTMLInputElement.cpp
+++ b/content/html/content/src/HTMLInputElement.cpp
@@ -5750,56 +5750,74 @@ HTMLInputElement::IsValidEmailAddressLis
 //static
 bool
 HTMLInputElement::IsValidEmailAddress(const nsAString& aValue)
 {
   nsAutoCString value = NS_ConvertUTF16toUTF8(aValue);
   uint32_t i = 0;
   uint32_t length = value.Length();
 
+  // Email addresses can't be empty and can't end with a '.' or '-'.
+  if (length == 0 || value[length - 1] == '.' || value[length - 1] == '-') {
+    return false;
+  }
+
+  uint32_t atPos = (uint32_t)value.FindChar('@');
+  // Email addresses must contain a '@', but can't begin or end with it.
+  if (atPos == (uint32_t)kNotFound || atPos == 0 || atPos == length - 1) {
+    return false;
+  }
+
   // Puny-encode the string if needed before running the validation algorithm.
   nsCOMPtr<nsIIDNService> idnSrv = do_GetService(NS_IDNSERVICE_CONTRACTID);
   if (idnSrv) {
+    // ConvertUTF8toACE() treats 'username@domain' as a single label so we need
+    // to puny-encode the username and domain parts separately.
+    const nsDependentCSubstring username = Substring(value, 0, atPos);
     bool ace;
-    if (NS_SUCCEEDED(idnSrv->IsACE(value, &ace)) && !ace) {
-      nsAutoCString punyCodedValue;
-      if (NS_SUCCEEDED(idnSrv->ConvertUTF8toACE(value, punyCodedValue))) {
-        value = punyCodedValue;
-        length = value.Length();
+    if (NS_SUCCEEDED(idnSrv->IsACE(username, &ace)) && !ace) {
+      nsAutoCString usernameACE;
+      // TODO: Bug 901347: Usernames longer than 63 chars are not converted by
+      // ConvertUTF8toACE(). For now, continue on even if the conversion fails.
+      if (NS_SUCCEEDED(idnSrv->ConvertUTF8toACE(username, usernameACE))) {
+        value.Replace(0, atPos, usernameACE);
+        atPos = usernameACE.Length();
       }
     }
+
+    const nsDependentCSubstring domain = Substring(value, atPos + 1);
+    if (NS_SUCCEEDED(idnSrv->IsACE(domain, &ace)) && !ace) {
+      nsAutoCString domainACE;
+      if (NS_FAILED(idnSrv->ConvertUTF8toACE(domain, domainACE))) {
+        return false;
+      }
+      value.Replace(atPos + 1, domain.Length(), domainACE);
+    }
+
+    length = value.Length();
   } else {
     NS_ERROR("nsIIDNService isn't present!");
   }
 
-  // If the email address is empty, begins with an '@'
-  // or ends with a '.' or '-', we know it's invalid.
-  if (length == 0 || value[0] == '@' || value[length-1] == '.' ||
-      value[length-1] == '-') {
-    return false;
-  }
-
   // Parsing the username.
-  for (; i < length && value[i] != '@'; ++i) {
+  for (; i < atPos; ++i) {
     PRUnichar c = value[i];
 
     // The username characters have to be in this list to be valid.
     if (!(nsCRT::IsAsciiAlpha(c) || nsCRT::IsAsciiDigit(c) ||
           c == '.' || c == '!' || c == '#' || c == '$' || c == '%' ||
           c == '&' || c == '\''|| c == '*' || c == '+' || c == '-' ||
           c == '/' || c == '=' || c == '?' || c == '^' || c == '_' ||
           c == '`' || c == '{' || c == '|' || c == '}' || c == '~' )) {
       return false;
     }
   }
 
-  // If there is no domain name, that's not a valid email address.
-  if (++i >= length) {
-    return false;
-  }
+  // Skip the '@'.
+  ++i;
 
   // The domain name can't begin with a dot or a dash.
   if (value[i] == '.' || value[i] == '-') {
     return false;
   }
 
   // Parsing the domain name.
   for (; i < length; ++i) {
--- a/content/html/content/test/forms/test_input_email.html
+++ b/content/html/content/test/forms/test_input_email.html
@@ -107,16 +107,30 @@ var values = [
   [ 'foo@b\rar.com', true ],
   [ 'foo@b\r\nar.com', true ],
   [ 'foo@.', false ],
   [ 'foo@fü.com', true ],
   [ 'foo@fu.cüm', true ],
   // Long strings with UTF-8.
   [ 'this.is.email.should.be.longer.than.sixty.four.characters.föö@mözillä.tld', true ],
   [ 'this-is-email-should-be-longer-than-sixty-four-characters-föö@mözillä.tld', true, true ],
+  // Long labels.
+  [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss', true ],
+  [ 'foo@thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', true ],
+  [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss.com', true ],
+  [ 'foo@foo.thislabelisexactly63characterssssssssssssssssssssssssssssssssss', true ],
+  [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', false ],
+  [ 'foo@thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', false ],
+  [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss.com', false ],
+  [ 'foo@foo.thislabelisexactly64charactersssssssssssssssssssssssssssssssssss', false ],
+  // Long labels with UTF-8.
+  [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', false ],
+  [ 'foo@thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', false ],
+  [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss.com', false ],
+  [ 'foo@foo.thisläbelisexäctly63charäcterssssssssssssssssssssssssssssssssss', false ],
   // The domains labels (sub-domains or tld) can't start or finish with a '-'
   [ 'foo@foo-bar', true ],
   [ 'foo@-foo', false ],
   [ 'foo@foo-.bar', false ],
   [ 'foo@-.-', false ],
   [ 'foo@fo-o.bar', true ],
   [ 'foo@fo-o.-bar', false ],
   [ 'foo@fo-o.bar-', false ],
@@ -174,17 +188,17 @@ legalCharacters = "abcdefghijklmnopqrstu
 legalCharacters += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
 legalCharacters += "0123456789";
 
 // Add domain legal characters (except '.' and '-' because they are special).
 for (c of legalCharacters) {
   values.push(["foo@foo.bar" + c, true]);
 }
 // Add the concatenation of all legal characters too.
-values.push(["foo@bar.com" + legalCharacters, true]);
+values.push(["foo@bar." + legalCharacters, true]);
 
 // Add domain illegal characters.
 illegalCharacters = "()<>[]:;@\\,!#$%&'*+/=?^_`{|}~ \t";
 for (c of illegalCharacters) {
   values.push(['foo@foo.ba' + c + 'r', false]);
 }
 
 values.forEach(function([value, valid, todo]) {