Bug 1552866 - Restore prefilling user name in account creation after bug 1433350. r=mkmelin,jorgk
authorNeil Rashbrook <neil@parkwaycc.co.uk>
Mon, 03 Jun 2019 00:39:11 +0200
changeset 35744 1fc57303ad6bd7a1dec39fed8c952c44c595bc0a
parent 35743 1579379c18bb86f01a8ed6cae1a99b0f79aa6c57
child 35745 c104316e6c2120b3bff9498631063a37d54530b6
push id392
push userclokep@gmail.com
push dateMon, 02 Sep 2019 20:17:19 +0000
reviewersmkmelin, jorgk
bugs1552866, 1433350
Bug 1552866 - Restore prefilling user name in account creation after bug 1433350. r=mkmelin,jorgk
common/public/moz.build
common/public/nsIUserInfo.idl
common/src/moz.build
common/src/nsCommonModule.cpp
common/src/nsUserInfo.h
common/src/nsUserInfoMac.mm
common/src/nsUserInfoUnix.cpp
common/src/nsUserInfoWin.cpp
mail/components/accountcreation/content/emailWizard.js
mail/components/newmailaccount/content/accountProvisioner.js
mailnews/base/prefs/content/aw-identity.js
--- a/common/public/moz.build
+++ b/common/public/moz.build
@@ -1,14 +1,15 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPIDL_SOURCES += [
     'nsIComponentManagerExtra.idl',
+    'nsIUserInfo.idl',
 ]
 
 XPIDL_MODULE = 'msgcommonbase'
 
 EXPORTS += [
     'nsCommonBaseCID.h'
 ]
new file mode 100644
--- /dev/null
+++ b/common/public/nsIUserInfo.idl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+/**
+ * These are things the system may know about the current user.
+ */
+[scriptable, uuid(6c1034f0-1dd2-11b2-aa14-e6657ed7bb0b)]
+interface nsIUserInfo : nsISupports
+{
+    readonly attribute AString fullname;
+
+    readonly attribute AString emailAddress;
+
+    readonly attribute AString username;
+
+    readonly attribute AString domain;
+};
+
+%{C++
+
+// 14c13684-1dd2-11b2-9463-bb10ba742554
+#define NS_USERINFO_CID \
+{  0x14c13684, 0x1dd2, 0x11b2, \
+  {0x94, 0x63, 0xbb, 0x10, 0xba, 0x74, 0x25, 0x54}}
+
+#define NS_USERINFO_CONTRACTID "@mozilla.org/userinfo;1"
+
+%}
--- a/common/src/moz.build
+++ b/common/src/moz.build
@@ -9,16 +9,30 @@ EXTRA_JS_MODULES += [
     'Overlays.jsm',
 ]
 
 SOURCES += [
     'nsCommonModule.cpp',
     'nsComponentManagerExtra.cpp',
 ]
 
+if CONFIG['OS_ARCH'] == 'WINNT':
+    # This file cannot be built in unified mode because of name clashes with Windows headers.
+    SOURCES += [
+        'nsUserInfoWin.cpp',
+    ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    SOURCES += [
+        'nsUserInfoMac.mm',
+    ]
+else:
+    SOURCES += [
+        'nsUserInfoUnix.cpp',
+    ]
+
 LOCAL_INCLUDES += [
     '/%s/netwerk/base' % CONFIG['mozreltopsrcdir']
 ]
 
 FINAL_LIBRARY = 'xul'
 
 if CONFIG['MOZ_CALENDAR']:
     DEFINES['MOZ_CALENDAR'] = True
--- a/common/src/nsCommonModule.cpp
+++ b/common/src/nsCommonModule.cpp
@@ -4,16 +4,17 @@
 
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/TransactionManager.h"
 #include "nsBaseCommandController.h"
 #include "nsCommonBaseCID.h"
 #include "nsComponentManagerExtra.h"
 #include "nsSyncStreamListener.h"
 #include "nsSAXXMLReader.h"  // Sax parser.
+#include "nsUserInfo.h"
 #include "nsXULAppAPI.h"
 
 using mozilla::TransactionManager;
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsComponentManagerExtra)
 NS_DEFINE_NAMED_CID(NS_COMPONENTMANAGEREXTRA_CID);
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsBaseCommandController)
@@ -22,16 +23,19 @@ NS_DEFINE_NAMED_CID(NS_BASECOMMANDCONTRO
 NS_GENERIC_FACTORY_CONSTRUCTOR(TransactionManager)
 NS_DEFINE_NAMED_CID(NS_TRANSACTIONMANAGER_CID);
 
 NS_DEFINE_NAMED_CID(NS_SYNCSTREAMLISTENER_CID);
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSAXXMLReader)
 NS_DEFINE_NAMED_CID(NS_SAXXMLREADER_CID);
 
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsUserInfo)
+NS_DEFINE_NAMED_CID(NS_USERINFO_CID);
+
 static nsresult CreateNewSyncStreamListener(nsISupports *aOuter, REFNSIID aIID,
                                             void **aResult) {
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = nullptr;
 
   if (aOuter) {
     return NS_ERROR_NO_AGGREGATION;
   }
@@ -46,24 +50,26 @@ const mozilla::Module::CIDEntry kCommonC
     {&kNS_COMPONENTMANAGEREXTRA_CID, false, nullptr,
      nsComponentManagerExtraConstructor},
     {&kNS_BASECOMMANDCONTROLLER_CID, false, nullptr,
      nsBaseCommandControllerConstructor},
     {&kNS_TRANSACTIONMANAGER_CID, false, nullptr,
      TransactionManagerConstructor},
     {&kNS_SYNCSTREAMLISTENER_CID, false, nullptr, CreateNewSyncStreamListener},
     {&kNS_SAXXMLREADER_CID, false, nullptr, nsSAXXMLReaderConstructor},
+    {&kNS_USERINFO_CID, false, nullptr, nsUserInfoConstructor},
     {nullptr}};
 
 const mozilla::Module::ContractIDEntry kCommonContracts[] = {
     {NS_COMPONENTMANAGEREXTRA_CONTRACTID, &kNS_COMPONENTMANAGEREXTRA_CID},
     {NS_BASECOMMANDCONTROLLER_CONTRACTID, &kNS_BASECOMMANDCONTROLLER_CID},
     {NS_TRANSACTIONMANAGER_CONTRACTID, &kNS_TRANSACTIONMANAGER_CID},
     {NS_SYNCSTREAMLISTENER_CONTRACTID, &kNS_SYNCSTREAMLISTENER_CID},
     {NS_SAXXMLREADER_CONTRACTID, &kNS_SAXXMLREADER_CID},
+    {NS_USERINFO_CONTRACTID, &kNS_USERINFO_CID},
     {nullptr}};
 
 static const mozilla::Module kCommonModule = {mozilla::Module::kVersion,
                                               kCommonCIDs,
                                               kCommonContracts,
                                               nullptr,
                                               nullptr,
                                               nullptr,
new file mode 100644
--- /dev/null
+++ b/common/src/nsUserInfo.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#ifndef __nsUserInfo_h
+#define __nsUserInfo_h
+
+#include "nsIUserInfo.h"
+
+class nsUserInfo : public nsIUserInfo
+
+{
+ public:
+  nsUserInfo(void);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIUSERINFO
+
+ protected:
+  virtual ~nsUserInfo();
+};
+
+#endif /* __nsUserInfo_h */
new file mode 100644
--- /dev/null
+++ b/common/src/nsUserInfoMac.mm
@@ -0,0 +1,70 @@
+/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsUserInfo.h"
+#include "nsObjCExceptions.h"
+#include "nsString.h"
+#include "nsCocoaUtils.h"
+
+#import <Cocoa/Cocoa.h>
+#import <AddressBook/AddressBook.h>
+
+NS_IMPL_ISUPPORTS(nsUserInfo, nsIUserInfo)
+
+nsUserInfo::nsUserInfo() {}
+
+nsUserInfo::~nsUserInfo() {}
+
+NS_IMETHODIMP
+nsUserInfo::GetFullname(nsAString& aFullname) {
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK
+
+  nsCocoaUtils::GetStringForNSString(NSFullUserName(), aFullname);
+
+  NS_OBJC_END_TRY_ABORT_BLOCK
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetUsername(nsAString& aUsername) {
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK
+
+  nsCocoaUtils::GetStringForNSString(NSUserName(), aUsername);
+
+  NS_OBJC_END_TRY_ABORT_BLOCK
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetEmailAddress(nsAString& aEmailAddress) {
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK
+
+  aEmailAddress.Truncate();
+  // Try to get this user's primary email from the system addressbook's "me card"
+  // (if they've filled it)
+  ABPerson* me = [[ABAddressBook sharedAddressBook] me];
+  ABMultiValue* emailAddresses = [me valueForProperty:kABEmailProperty];
+  if ([emailAddresses count] > 0) {
+    // get the index of the primary email, in case there are more than one
+    int primaryEmailIndex = [emailAddresses indexForIdentifier:[emailAddresses primaryIdentifier]];
+    nsCocoaUtils::GetStringForNSString([emailAddresses valueAtIndex:primaryEmailIndex],
+                                       aEmailAddress);
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetDomain(nsAString& aDomain) {
+  GetEmailAddress(aDomain);
+  int32_t index = aDomain.FindChar('@');
+  if (index != -1) {
+    // chop off everything before, and including the '@'
+    aDomain.Cut(0, index + 1);
+  }
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/common/src/nsUserInfoUnix.cpp
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsUserInfo.h"
+#include "nsCRT.h"
+
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsNativeCharsetUtils.h"
+
+nsUserInfo::nsUserInfo() {}
+
+nsUserInfo::~nsUserInfo() {}
+
+NS_IMPL_ISUPPORTS(nsUserInfo, nsIUserInfo)
+
+NS_IMETHODIMP
+nsUserInfo::GetFullname(nsAString& aFullname) {
+  aFullname.Truncate();
+  struct passwd* pw = nullptr;
+
+  pw = getpwuid(geteuid());
+
+  if (!pw || !pw->pw_gecos) return NS_OK;
+
+  nsAutoCString fullname(pw->pw_gecos);
+
+  // now try to parse the GECOS information, which will be in the form
+  // Full Name, <other stuff> - eliminate the ", <other stuff>
+  // also, sometimes GECOS uses "&" to mean "the user name" so do
+  // the appropriate substitution
+
+  // truncate at first comma (field delimiter)
+  int32_t index;
+  if ((index = fullname.Find(",")) != kNotFound) fullname.Truncate(index);
+
+  // replace ampersand with username
+  if (pw->pw_name) {
+    nsAutoCString username(pw->pw_name);
+    if (!username.IsEmpty() && nsCRT::IsLower(username.CharAt(0)))
+      username.SetCharAt(nsCRT::ToUpper(username.CharAt(0)), 0);
+
+    fullname.ReplaceSubstring("&", username.get());
+  }
+
+  CopyUTF8toUTF16(fullname, aFullname);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetUsername(nsAString& aUsername) {
+  aUsername.Truncate();
+  struct passwd* pw = nullptr;
+
+  // is this portable?  those are POSIX compliant calls, but I need to check
+  pw = getpwuid(geteuid());
+
+  if (!pw || !pw->pw_name) return NS_OK;
+
+  CopyUTF8toUTF16(mozilla::MakeStringSpan(pw->pw_name), aUsername);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetDomain(nsAString& aDomain) {
+  aDomain.Truncate();
+  struct utsname buf;
+  char* domainname = nullptr;
+
+  if (uname(&buf) < 0) {
+    return NS_OK;
+  }
+
+#if defined(__linux__)
+  domainname = buf.domainname;
+#endif
+
+  if (domainname && domainname[0]) {
+    CopyUTF8toUTF16(mozilla::MakeStringSpan(domainname), aDomain);
+  } else {
+    // try to get the hostname from the nodename
+    // on machines that use DHCP, domainname may not be set
+    // but the nodename might.
+    if (buf.nodename[0]) {
+      // if the nodename is foo.bar.org, use bar.org as the domain
+      char* pos = strchr(buf.nodename, '.');
+      if (pos) {
+        CopyUTF8toUTF16(mozilla::MakeStringSpan(pos + 1), aDomain);
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetEmailAddress(nsAString& aEmailAddress) {
+  // use username + "@" + domain for the email address
+
+  nsString username;
+  nsString domain;
+
+  GetUsername(username);
+  GetDomain(domain);
+
+  if (!username.IsEmpty() && !domain.IsEmpty()) {
+    aEmailAddress = username;
+    aEmailAddress.Append('@');
+    aEmailAddress += domain;
+  } else {
+    aEmailAddress.Truncate();
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/common/src/nsUserInfoWin.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsUserInfo.h"
+
+#include "mozilla/ArrayUtils.h"  // ArrayLength
+#include "nsString.h"
+#include "windows.h"
+#include "nsCRT.h"
+
+#define SECURITY_WIN32
+#include "lm.h"
+#include "security.h"
+
+nsUserInfo::nsUserInfo() {}
+
+nsUserInfo::~nsUserInfo() {}
+
+NS_IMPL_ISUPPORTS(nsUserInfo, nsIUserInfo)
+
+NS_IMETHODIMP
+nsUserInfo::GetUsername(nsAString& aUsername) {
+  aUsername.Truncate();
+
+  // UNLEN is the max username length as defined in lmcons.h
+  wchar_t username[UNLEN + 1];
+  DWORD size = mozilla::ArrayLength(username);
+  if (!GetUserNameW(username, &size)) return NS_OK;
+
+  aUsername.Assign(username);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetFullname(nsAString& aFullname) {
+  aFullname.Truncate();
+
+  wchar_t fullName[512];
+  DWORD size = mozilla::ArrayLength(fullName);
+
+  if (GetUserNameExW(NameDisplay, fullName, &size)) {
+    aFullname.Assign(fullName);
+  } else {
+    // Try to use the net APIs regardless of the error because it may be
+    // able to obtain the information.
+    wchar_t username[UNLEN + 1];
+    size = mozilla::ArrayLength(username);
+    if (!GetUserNameW(username, &size)) {
+      return NS_OK;
+    }
+
+    const DWORD level = 2;
+    LPBYTE info;
+    // If the NetUserGetInfo function has no full name info it will return
+    // success with an empty string.
+    NET_API_STATUS status = NetUserGetInfo(nullptr, username, level, &info);
+    if (status != NERR_Success) {
+      return NS_OK;
+    }
+
+    aFullname.Assign(reinterpret_cast<USER_INFO_2*>(info)->usri2_full_name);
+    NetApiBufferFree(info);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetDomain(nsAString& aDomain) {
+  aDomain.Truncate();
+
+  const DWORD level = 100;
+  LPBYTE info;
+  NET_API_STATUS status = NetWkstaGetInfo(nullptr, level, &info);
+  if (status == NERR_Success) {
+    aDomain.Assign(reinterpret_cast<WKSTA_INFO_100*>(info)->wki100_langroup);
+    NetApiBufferFree(info);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetEmailAddress(nsAString& aEmailAddress) {
+  aEmailAddress.Truncate();
+
+  // RFC3696 says max length of an email address is 254
+  wchar_t emailAddress[255];
+  DWORD size = mozilla::ArrayLength(emailAddress);
+
+  if (!GetUserNameExW(NameUserPrincipal, emailAddress, &size)) {
+    return NS_OK;
+  }
+
+  aEmailAddress.Assign(emailAddress);
+  return NS_OK;
+}
--- a/mail/components/accountcreation/content/emailWizard.js
+++ b/mail/components/accountcreation/content/emailWizard.js
@@ -163,16 +163,23 @@ EmailConfigWizard.prototype = {
      * That means, before you actually use the config (e.g. to create an
      * account or to show it to the user), you need to run replaceVariables().
      */
     this._currentConfig = null;
 
     this._domain = "";
     this._email = "";
     this._realname = "";
+    if ("@mozilla.org/userinfo;1" in Cc) {
+      let userInfo = Cc["@mozilla.org/userinfo;1"].getService(Ci.nsIUserInfo);
+      // Assume that it's a genuine full name if it includes a space.
+      if (userInfo.fullname.includes(" ")) {
+        this._realname = userInfo.fullname;
+      }
+    }
     e("realname").value = this._realname;
     this._password = "";
     this._exchangeUsername = ""; // only for Exchange AutoDiscover and only if needed
     this._okCallback = null;
 
     if (window.arguments && window.arguments[0]) {
       if (window.arguments[0].msgWindow) {
         this._parentMsgWindow = window.arguments[0].msgWindow;
--- a/mail/components/newmailaccount/content/accountProvisioner.js
+++ b/mail/components/newmailaccount/content/accountProvisioner.js
@@ -279,18 +279,22 @@ var EmailAccountProvisioner = {
     // Link the keypress function to the name field so that we can enable and
     // disable the search button.
     let nameElement = document.getElementById("name");
     nameElement.addEventListener("keyup",
       EmailAccountProvisioner.onSearchInputOrProvidersChanged);
 
     // If we have a name stored in local storage from an earlier session,
     // populate the search field with it.
-    nameElement.value = EmailAccountProvisioner.storage.getItem("name") ||
-                        nameElement.value;
+    let name = EmailAccountProvisioner.storage.getItem("name") ||
+               nameElement.value;
+    if (!name && "@mozilla.org/userinfo;1" in Cc) {
+      name = Cc["@mozilla.org/userinfo;1"].getService(Ci.nsIUserInfo).fullname;
+    }
+    nameElement.value = name;
     EmailAccountProvisioner.saveName();
 
     // Pretend like we've typed something into the search input to set the
     // initial enabled/disabled state of the search button.
     EmailAccountProvisioner.onSearchInputOrProvidersChanged();
 
     document.getElementById("window").style.height = (window.innerHeight - 1) + "px";
 
--- a/mailnews/base/prefs/content/aw-identity.js
+++ b/mailnews/base/prefs/content/aw-identity.js
@@ -115,19 +115,24 @@ function setEmailDescriptionText() {
     // Create a text nodes with text to be displayed
     var emailDescTextNode       =  document.createTextNode(displayText);
 
     // Display the dynamically generated text for email description
     emailDescText.appendChild(emailDescTextNode);
 }
 
 function checkForFullName() {
-  // var name = document.getElementById("fullName");
-  // We currently have no way to propose any useful name here.
+  var name = document.getElementById("fullName");
+  if (name.value == "" && "@mozilla.org/userinfo;1" in Cc) {
+    name.value = Cc["@mozilla.org/userinfo;1"].getService(Ci.nsIUserInfo).fullname;
+  }
 }
 
 function checkForEmail() {
-    var email = document.getElementById("email");
-    var pageData = parent.GetPageData();
-    if (pageData && pageData.identity && pageData.identity.email) {
-        email.value = pageData.identity.email.value;
-    }
+  var email = document.getElementById("email");
+  var pageData = parent.GetPageData();
+  if (pageData && pageData.identity && pageData.identity.email) {
+    email.value = pageData.identity.email.value;
+  }
+  if (email.value == "" && "@mozilla.org/userinfo;1" in Cc) {
+    email.value = Cc["@mozilla.org/userinfo;1"].getService(Ci.nsIUserInfo).emailAddress;
+  }
 }