[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="®isterXMPP.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