[tor-commits] [tor-messenger-build/master] Add in-band XMPP registration support (#12097)

sukhbir at torproject.org sukhbir at torproject.org
Mon Jun 29 17:57:06 UTC 2015


commit c2f960e563837f6f9e56954cf4dd9f35489b2d13
Author: Sukhbir Singh <sukhbir at torproject.org>
Date:   Mon Jun 29 13:56:26 2015 -0400

    Add in-band XMPP registration support (#12097)
---
 projects/instantbird/config                        |    6 +-
 .../instantbird/xmpp-inband-registration.patch     |  278 ++++++++++++++++++++
 projects/instantbird/xmppRegister.js               |  127 +++++++++
 projects/instantbird/xmppRegister.xul              |   25 ++
 4 files changed, 435 insertions(+), 1 deletion(-)

diff --git a/projects/instantbird/config b/projects/instantbird/config
index 656cdb4..63348dc 100644
--- a/projects/instantbird/config
+++ b/projects/instantbird/config
@@ -79,6 +79,9 @@ input_files:
   - filename: cert_override.txt
   - filename: ctcp-time.patch
   - filename: ctcp-ping.patch
+  - filename: xmpp-inband-registration.patch
+  - filename: xmppRegister.js
+  - filename: xmppRegister.xul
   - filename: xmpp-domain.patch
   - filename: xmpp-resource.patch
   - filename: xmpp-onion-js.patch
@@ -100,10 +103,11 @@ input_files:
   - filename: branding/default.ico
   - filename: branding/instantbird.ico
   - filename: branding/name.patch
-  - filename: branding/osx.patch
   - filename: branding/instantbird.icns
   - filename: branding/credits.patch
   - filename: branding/about.png
+  - filename: branding/osx.patch
+    enable: '[% c("var/osx") %]
   - filename: fix-mingw-build.patch
     enable: '[% c("var/windows") %]'
   - filename: f2e7cea9bc6a-bug-1150967.patch
diff --git a/projects/instantbird/xmpp-inband-registration.patch b/projects/instantbird/xmpp-inband-registration.patch
new file mode 100644
index 0000000..73c838c
--- /dev/null
+++ b/projects/instantbird/xmpp-inband-registration.patch
@@ -0,0 +1,278 @@
+diff --git a/chat/locales/en-US/xmpp.properties b/chat/locales/en-US/xmpp.properties
+--- a/chat/locales/en-US/xmpp.properties
++++ b/chat/locales/en-US/xmpp.properties
+@@ -8,16 +8,19 @@
+ #   (These will be displayed in account.connection.progress from
+ #    accounts.properties, which adds … at the end, so do not include
+ #    periods at the end of these messages.)
+ connection.initializingStream=Initializing stream
+ connection.initializingEncryption=Initializing encryption
+ connection.authenticating=Authenticating
+ connection.gettingResource=Getting resource
+ connection.downloadingRoster=Downloading contact list
++connection.registering=Registering new account with server
++connection.gettingRegistration=Getting registration form
++connection.onRegistrationSuccess=Account registration successful
+ 
+ # LOCALIZATION NOTE (connection.error.*)
+ #   These will show in the account manager if an error occurs during the
+ #   connection attempt.
+ connection.error.invalidUsername=Invalid username (your username should contain an '@' character)
+ connection.error.failedToCreateASocket=Failed to create a socket (Are you offline?)
+ connection.error.serverClosedConnection=The server closed the connection
+ connection.error.resetByPeer=Connection reset by peer
+@@ -28,16 +31,18 @@ connection.error.startTLSRequired=The se
+ connection.error.startTLSNotSupported=The server doesn't support encryption but your configuration requires it
+ connection.error.failedToStartTLS=Failed to start encryption
+ connection.error.noAuthMec=No authentication mechanism offered by the server
+ connection.error.noCompatibleAuthMec=None of the authentication mechanisms offered by the server are supported
+ connection.error.notSendingPasswordInClear=The server only supports authentication by sending the password in cleartext
+ connection.error.authenticationFailure=Authentication failure
+ connection.error.notAuthorized=Not authorized (Did you enter the wrong password?)
+ connection.error.failedToGetAResource=Failed to get a resource
++connection.error.noRegistrationSupport=The server does not support in-band registration
++connection.error.registrationCancel=Registration canceled
+ 
+ 
+ # LOCALIZATION NOTE (conversation.error.notDelivered):
+ #   This is displayed in a conversation as an error message when a message
+ #   the user has sent wasn't delivered.
+ #   %S is replaced by the text of the message that wasn't delivered.
+ conversation.error.notDelivered=This message could not be delivered: %S
+ #   This is displayed in a conversation as an error message when joining a MUC
+diff --git a/chat/protocols/xmpp/xmpp-session.jsm b/chat/protocols/xmpp/xmpp-session.jsm
+--- a/chat/protocols/xmpp/xmpp-session.jsm
++++ b/chat/protocols/xmpp/xmpp-session.jsm
+@@ -6,16 +6,18 @@ const EXPORTED_SYMBOLS = ["XMPPSession",
+ 
+ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+ 
+ Cu.import("resource:///modules/imXPCOMUtils.jsm");
+ Cu.import("resource:///modules/socket.jsm");
+ Cu.import("resource:///modules/xmpp-xml.jsm");
+ Cu.import("resource:///modules/xmpp-authmechs.jsm");
+ 
++const registerWindow = "chrome://instantbird/content/xmppRegister.xul";
++
+ XPCOMUtils.defineLazyGetter(this, "_", function()
+   l10nHelper("chrome://chat/locale/xmpp.properties")
+ );
+ 
+ // Workaround because a lazy getter can't be exported.
+ XPCOMUtils.defineLazyGetter(this, "_defaultResource", function()
+   l10nHelper("chrome://branding/locale/brand.properties")("brandShortName")
+ );
+@@ -63,16 +65,17 @@ XMPPSession.prototype = {
+   __proto__: Socket,
+   connectTimeout: 60,
+   readWriteTimeout: 300,
+   sendPing: function() {
+     this.sendStanza(Stanza.iq("get", null, null,
+                               Stanza.node("ping", Stanza.NS.ping)),
+                     this.cancelDisconnectTimer, this);
+   },
++  nodes: {},
+   _lastReceiveTime: 0,
+   _lastSendTime: 0,
+   checkPingTimer(aJustSentSomething = false) {
+     // Don't start a ping timer if we're not fully connected yet.
+     if (this.onXmppStanza != this.stanzaListeners.accountListening)
+       return;
+     let now = Date.now();
+     if (aJustSentSomething)
+@@ -265,28 +268,95 @@ XMPPSession.prototype = {
+                      _("connection.error.startTLSNotSupported"));
+         return;
+       }
+ 
+       // If we aren't starting TLS, jump to the auth step.
+       this.onXmppStanza = this.stanzaListeners.startAuth;
+       this.onXmppStanza(aStanza);
+     },
++    onRegisterResponse: function(aStanza) {
++      let error = this._account.parseError(aStanza);
++      if (error) {
++        this.onError(null, aStanza.getElement(["error"]).innerText);
++        return;
++      }
++      if (aStanza.attributes["type"] == "result") {
++        this._account.reportConnecting(_("connection.onRegistrationSuccess"));
++        this._account.prefs.setBoolPref("register", false);
++        this._account.connect();
++      }
++      return;
++    },
++    startRegister: function(aStanza) {
++      // Some servers do not support in-band registration. In that case,
++      // complain and quit the registration process.
++      let error = this._account.parseError(aStanza);
++      if (error) {
++        this.onError(null, _("connection.error.noRegistrationSupport"));
++        return;
++      }
++
++      this._account.reportConnecting(_("connection.gettingRegistration"));
++      let registerStanza = aStanza.getChildrenByNS(Stanza.NS.register)[0];
++      // If we get registration data, show the form, else quit.
++      if (registerStanza.getElement(["x"])) {
++        registerStanza.wrappedJSObject = registerStanza;
++        let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]
++                   .getService(Ci.nsIWindowWatcher);
++        let win = ww.openWindow(null, registerWindow, "",
++                                "centerscreen,chrome,modal,minimizable=no", registerStanza);
++      } else {
++        this.onError(null, _("connection.error.noRegistrationSupport"));
++      }
++
++      // If the user cancelled the form, we should stop the registration.
++      if (this.nodes["cancel"])
++        this.onError(null, _("connection.error.registrationCancel"));
++
++      let xml = '<?xml version="1.0"?>';
++      let fieldNodes = [];
++      for (let key in this.nodes) {
++        let node = Stanza.node("field", null, {"var": key});
++        let childNode = Stanza.node("value");
++        childNode.addText(this.nodes[key]);
++        node.addChild(childNode);
++        fieldNodes.push(node);
++      }
++      let registerResponse = Stanza.iq("set", null, this._domain,
++                                       Stanza.node("query", Stanza.NS.register, null,
++                                                   Stanza.node("x", Stanza.NS.xdata,
++                                                               {"type": "submit"}, fieldNodes)));
++      this.sendStanza(registerResponse);
++      this.onXmppStanza = this.stanzaListeners.onRegisterResponse;
++    },
+     startTLS: function(aStanza) {
+       if (aStanza.localName != "proceed") {
+         this._networkError(_("connection.error.failedToStartTLS"));
+         return;
+       }
+ 
+       this.startTLS();
+       this._encrypted = true;
+       this.startStream();
+       this.onXmppStanza = this.stanzaListeners.startAuth;
+     },
+     startAuth: function(aStanza) {
++      // If the user has requested for a new account, we try to perform
++      // in-band registration first (if the server supports it) and then
++      // we authenticate.
++      if (this._account.getBool("register")) {
++        this._account.reportConnecting(_("connection.registering"));
++        let register = Stanza.iq("get", null, null,
++                                 Stanza.node("query", Stanza.NS.register));
++        this.sendStanza(register);
++        this.onXmppStanza = this.stanzaListeners.startRegister;
++        return;
++      }
++
+       if (aStanza.localName != "features") {
+         this.ERROR("Unexpected stanza " + aStanza.localName + ", expected 'features'");
+         this._networkError(_("connection.error.incorrectResponse"));
+         return;
+       }
+ 
+       let mechs = aStanza.getElement(["mechanisms"]);
+       if (!mechs) {
+diff --git a/im/content/accountWizard.js b/im/content/accountWizard.js
+--- a/im/content/accountWizard.js
++++ b/im/content/accountWizard.js
+@@ -106,16 +106,20 @@ var accountWizard = {
+   },
+ 
+   showUsernamePage: function aw_showUsernamePage() {
+     let proto = this.proto.id;
+     if ("userNameBoxes" in this && this.userNameProto == proto) {
+       this.checkUsername();
+       return;
+     }
++    
++    if (this.proto.id == "prpl-jabber") {
++      document.getElementById("registerXMPP").hidden = false;
++    }
+ 
+     let bundle = document.getElementById("accountsBundle");
+     let usernameInfo;
+     let emptyText = this.proto.usernameEmptyText;
+     if (emptyText) {
+       usernameInfo =
+         bundle.getFormattedString("accountUsernameInfoWithDescription",
+                                   [emptyText, this.proto.name]);
+@@ -412,16 +416,18 @@ var accountWizard = {
+   createAccount: function aw_createAccount() {
+     let acc = Services.accounts.createAccount(this.username, this.proto.id);
+     if (!this.proto.noPassword && this.password)
+       acc.password = this.password;
+     if (this.alias)
+       acc.alias = this.alias;
+     //FIXME: newMailNotification
+ 
++    acc.setBool("register", document.getElementById("registerXMPP").checked);
++
+     for (let i = 0; i < this.prefs.length; ++i) {
+       let option = this.prefs[i];
+       let opt = option.opt;
+       switch(opt.type) {
+       case opt.typeBool:
+         acc.setBool(option.name, option.value);
+         break;
+       case opt.typeInt:
+diff --git a/im/content/accountWizard.xul b/im/content/accountWizard.xul
+--- a/im/content/accountWizard.xul
++++ b/im/content/accountWizard.xul
+@@ -60,16 +60,17 @@
+               onpageshow="accountWizard.showUsernamePage();"
+               onpagehide="accountWizard.hideUsernamePage();"
+               onpagerewound="return accountWizard.rewindFromUsernamePage();">
+     <description id="usernameInfo"/>
+     <separator/>
+     <vbox id="userNameBox"/>
+     <separator/>
+     <description id="duplicateAccount" hidden="true">&accountUsernameDuplicate.label;</description>
++    <checkbox id="registerXMPP" label="&registerXMPP.label;" hidden="true" />
+   </wizardpage>
+ 
+   <wizardpage id="accountpassword" pageid="accountpassword" next="accountadvanced"
+               label="&accountPasswordTitle.label;">
+     <description>&accountPasswordInfo.label;</description>
+     <separator/>
+     <hbox id="passwordBox" align="baseline">
+       <label value="&accountPasswordField.label;" control="password" id="passwordLabel"/>
+diff --git a/im/content/jar.mn b/im/content/jar.mn
+--- a/im/content/jar.mn
++++ b/im/content/jar.mn
+@@ -56,16 +56,18 @@ instantbird.jar:
+ 	content/instantbird/proxies.css
+ 	content/instantbird/proxy.xml
+ *	content/instantbird/tabbrowser.xml
+ 	content/instantbird/tabbrowser.css
+ 	content/instantbird/utilities.js
+ *	content/instantbird/viewlog.xul
+ 	content/instantbird/viewlog.js
+ 	content/instantbird/viewlog.css
++*   content/instantbird/xmppRegister.xul
++    content/instantbird/xmppRegister.js
+ #ifdef XP_MACOSX
+ *	content/instantbird/hiddenWindow.xul
+ 	content/instantbird/menus-mac.xul
+ 	content/instantbird/macgestures.js
+ *	content/instantbird/jsConsoleOverlay.xul
+ *	content/instantbird/softwareUpdateOverlay.xul
+ #elifdef XP_WIN
+ 	content/instantbird/menus-win.xul
+diff --git a/im/locales/en-US/chrome/instantbird/accountWizard.dtd b/im/locales/en-US/chrome/instantbird/accountWizard.dtd
+--- a/im/locales/en-US/chrome/instantbird/accountWizard.dtd
++++ b/im/locales/en-US/chrome/instantbird/accountWizard.dtd
+@@ -26,8 +26,10 @@
+ <!ENTITY accountAliasInfo.label       "This will only be displayed in your conversations when you talk, remote buddies won't see it.">
+ <!ENTITY accountProxySettings.caption "Proxy Settings">
+ <!ENTITY accountProxySettings.change.label     "Change…">
+ <!ENTITY accountProxySettings.change.accessKey "C">
+ 
+ <!ENTITY accountSummaryTitle.label   "Summary">
+ <!ENTITY accountSummaryInfo.label    "A summary of the information you entered is displayed below. Please check it before the account is created.">
+ <!ENTITY accountSummary.connectAutomatically.label "Connect this account automatically.">
++
++<!ENTITY registerXMPP.label "Create this new account on the server">
diff --git a/projects/instantbird/xmppRegister.js b/projects/instantbird/xmppRegister.js
new file mode 100644
index 0000000..052fa6c
--- /dev/null
+++ b/projects/instantbird/xmppRegister.js
@@ -0,0 +1,127 @@
+const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
+
+Cu.import("resource:///modules/imXPCOMUtils.jsm");
+Cu.import("resource:///modules/xmpp-session.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "_", function()
+  l10nHelper("chrome://branding/locale/brand.properties")
+);
+
+let registerAccount = {
+  createElement: function(aType, aID, aValue) {
+    let element = document.createElement(aType);
+    if (aID)
+      element.setAttribute("id", aID);
+    if (aValue)
+      element.setAttribute("value", aValue);
+    return element;
+  },
+
+  createRow: function() {
+    let row = document.createElement("row");
+    row.setAttribute("align", "baseline");
+    return row;
+  },
+
+  onLoad: function() {
+    document.documentElement.getButton("accept").disabled = true;
+
+    this.rows = document.getElementById("register-rows");
+
+    this.nodes = XMPPSession.prototype.nodes;
+    this.registerStanza = window.arguments[0].wrappedJSObject;
+    this.dataStanza = this.registerStanza.getElement(["x"]);
+
+    let instructions = this.dataStanza.getElement(["instructions"]);
+    if (instructions) {
+      let instructionRow = this.createRow();
+      let instructionLabel = this.createElement("label", null, instructions.innerText);
+      instructionRow.appendChild(instructionLabel);
+      this.rows.appendChild(instructionRow);
+    }
+
+    let title = this.dataStanza.getElement(["title"]);
+    if (title)
+      document.title = title.innerText;
+    else
+      document.title = _("brandShortName");
+
+    for each (let ele in this.dataStanza.getElements(["field"])) {
+      let fieldType = ele.attributes["type"];
+      switch (fieldType) {
+
+        case "text-single":
+        case "text-private":
+
+          let textRow = this.createRow();
+          let textLabel = this.createElement("label", null, ele.attributes["label"]);
+
+          let textBox = this.createElement("textbox", ele.attributes["var"],
+                                           ele.getElement(["value"]) ? ele.getElement(["value"]).innerText : "");
+          if (fieldType == "text-private")
+            textBox.setAttribute("type", "password");
+          if (ele.getElement(["required"]))
+            textBox.setAttribute("oninput", "onInput(this)");
+
+          textRow.appendChild(textLabel);
+          textRow.appendChild(textBox);
+          this.rows.appendChild(textRow);
+          break;
+
+        case "fixed":
+
+          let fixedRow = this.createRow();
+          let fixedLabel = this.createElement("label", null, ele.getElement(["value"]).innerText);
+          fixedRow.appendChild(fixedLabel);
+          this.rows.appendChild(fixedRow);
+          break;
+      }
+    }
+
+    // Some forms have an OCR field. In that case, show the OCR image
+    // and provide input for the same.
+    let ocr = this.dataStanza.getElements(["field"]).find(e => e.attributes["var"] == "ocr");
+    if (ocr) {
+      let ocrRow = this.createRow();
+      let ocrImage = this.createElement("image");
+      ocrImage.setAttribute("src", "data:image/png;base64," + this.registerStanza.getElement(["data"]).innerText);
+
+      let ocrLabel = this.createElement("label", null, ocr.attributes["label"]);
+      let ocrInput = this.createElement("textbox", ocr.attributes["var"], null);
+
+      ocrRow.appendChild(ocrLabel);
+      ocrRow.appendChild(ocrInput);
+      this.rows.appendChild(ocrRow);
+
+      let ocrBox = document.createElement("hbox");
+      ocrBox.setAttribute("align", "center");
+      ocrBox.appendChild(ocrImage);
+
+      let ocrImageRow = this.createRow();
+      ocrImageRow.appendChild(ocrBox);
+      this.rows.appendChild(ocrImageRow);
+    }
+  },
+
+  onSave: function() {
+    for each (let elements in this.dataStanza.getElements(["field"])) {
+      if (elements.attributes["var"] != undefined) {
+        let variable = elements.attributes["var"];
+        if (document.getElementById(variable))
+          this.nodes[variable] = document.getElementById(variable).value;
+        else
+          this.nodes[variable] = elements.getElement(["value"]).innerText;
+      }
+    }
+    delete this.nodes["cancel"];
+  },
+  
+  onCancel: function() {
+    // The form was cancelled so we quit the registration.
+    this.nodes["cancel"] = true;
+  },
+};
+
+function onInput(e) {
+  document.documentElement.getButton("accept").disabled = !e.value;
+}
diff --git a/projects/instantbird/xmppRegister.xul b/projects/instantbird/xmppRegister.xul
new file mode 100644
index 0000000..c21bc96
--- /dev/null
+++ b/projects/instantbird/xmppRegister.xul
@@ -0,0 +1,25 @@
+<?xml version="1.0" ?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css" ?>
+
+<dialog
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  id="registerDialog"
+  onload="registerAccount.onLoad()"
+  buttons="accept,cancel"
+  ondialogaccept="return registerAccount.onSave()"
+  ondialogcancel="registerAccount.onCancel()">
+
+  <script type="application/javascript" src="chrome://instantbird/content/xmppRegister.js" />
+
+  <grid flex="1">
+
+    <columns>
+      <column flex="1" />
+      <column flex="1" />
+    </columns>
+
+    <rows id="register-rows" />
+
+  </grid>
+
+</dialog>



More information about the tor-commits mailing list