diff --git a/Gruntfile.js b/Gruntfile.js index 5bfc8cc5..6cac4725 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -127,7 +127,8 @@ module.exports = function(grunt) { sass: { stylesheets: { files: { - 'stylesheets/manifest.css': 'stylesheets/manifest.scss' + 'stylesheets/manifest.css': 'stylesheets/manifest.scss', + 'stylesheets/options.css': 'stylesheets/options.scss' } } }, diff --git a/images/appstore.svg b/images/appstore.svg new file mode 100644 index 00000000..ac111e59 --- /dev/null +++ b/images/appstore.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/images/playstore.png b/images/playstore.png new file mode 100644 index 00000000..9a50affd Binary files /dev/null and b/images/playstore.png differ diff --git a/js/options.js b/js/options.js index ba774c26..e9df26ae 100644 --- a/js/options.js +++ b/js/options.js @@ -13,176 +13,52 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - ;(function() { - function validateCode() { - var verificationCode = $('#code').val().replace(/\D/g, ''); - if (verificationCode.length == 6) { - return verificationCode; - } - } - - function displayError(error) { - $('#error').hide().text(error).addClass('in').fadeIn(); - } - + 'use strict'; $(function() { - var phoneView = new Whisper.PhoneInputView({el: $('#phone-number-input')}); - phoneView.render(); if (textsecure.registration.isDone()) { - $('#complete-number').text(textsecure.utils.unencodeNumber(textsecure.storage.getUnencrypted("number_id"))[0]);//TODO: no + $('#complete-number').text( + textsecure.utils.unencodeNumber( + textsecure.storage.getUnencrypted("number_id") + )[0] + );//TODO: no $('#setup-complete').show().addClass('in'); } else { - $('#choose-setup').show().addClass('in'); - - $('input.number').on('validation', function() { - if ($('#number-container').hasClass('valid')) { - $('#request-sms, #request-voice').removeAttr('disabled'); - } else { - $('#request-sms, #request-voice').prop('disabled', 'disabled'); - } - }); - - $('#code').on('change', function() { - if (!validateCode()) - $('#code').addClass('invalid'); - else - $('#code').removeClass('invalid'); - }); - - $('#request-voice').click(function() { - $('#error').hide(); - var number = phoneView.validateNumber(); - if (number) { - textsecure.api.requestVerificationVoice(number).catch(displayError); - $('#step2').addClass('in').fadeIn(); - } else { - $('#number-container').addClass('invalid'); - } - }); - - $('#request-sms').click(function() { - $('#error').hide(); - var number = phoneView.validateNumber(); - if (number) { - textsecure.api.requestVerificationSMS(number).catch(displayError); - $('#step2').addClass('in').fadeIn(); - } else { - $('#number-container').addClass('invalid'); - } - }); - - $('#new-account').click(function(){ - $('#choose-setup').fadeOut(function() { - $('#single-device').addClass('in').fadeIn(); - }); - - $('#single-device .back').click(function() { - $('#single-device').fadeOut(function() { - $('#choose-setup').addClass('in').fadeIn(); - $('input.number').removeClass('invalid'); - }); - }); - - $('#single-device form').submit(function(e) { - e.preventDefault(); - $('#error').hide(); - var number = phoneView.validateNumber(); - var verificationCode = validateCode(); - if (number && verificationCode) { - $('#verifyCode').prop('disabled', 'disabled'); - $('#verify *').hide(); - $('#verify').show().addClass('in'); - $('#verify2').show(); - - textsecure.registerSingleDevice(number, verificationCode, function(step) { + $('#init-setup').show().addClass('in'); + $('#status').text("Connecting..."); + axolotl.protocol.createIdentityKeyRecvSocket().then(function(cryptoInfo) { + var qrCode = new QRCode(document.getElementById('qr')); + var socket = textsecure.api.getTempWebsocket(); + new WebSocketResource(socket, function(request) { + if (request.path == "/v1/address" && request.verb == "PUT") { + var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body); + var url = [ 'tsdevice:/', '?uuid=', proto.uuid, '&pub_key=', + encodeURIComponent(btoa(getString(cryptoInfo.pubKey))) ].join(''); + $('#status').text(''); + qrCode.makeCode(url); + request.respond(200, 'OK'); + } else if (request.path == "/v1/message" && request.verb == "PUT") { + $('#qr').hide(); + textsecure.registerSecondDevice(request.body, cryptoInfo, function(step) { switch(step) { case 1: - $('#verify3').show(); + $('#status').text('Registering new device...'); break; case 2: - $('#verify4').show(); + $('#status').text('Generating keys...'); break; case 3: - $('#complete-number').text(number); - $('#verify').hide(); - $('#init-setup').hide().removeClass('in'); - $('#setup-complete').show().addClass('in'); + $('#status').text('Uploading keys...'); + break; + case 4: + $('#status').text('All done!'); textsecure.registration.done(); + $('#init-setup').hide(); + $('#setup-complete').show().addClass('in'); } - }).catch(function(error) { - $('#verify *').hide(); - $('#verifyCode').removeAttr('disabled'); - if (error.humanError) - displayError(error.humanError); - else - displayError(error); //XXX }); - } - }); - }); - - $('#new-device').click(function(){ - $('#choose-setup').fadeOut(function() { - $('#multi-device').addClass('in').fadeIn(); - }); - - $('#multi-device .back').click(function() { - $('#multi-device').fadeOut(function() { - $('#choose-setup').addClass('in').fadeIn(); - $('input.number').removeClass('invalid'); - }); - }); - - $('#multi-device .status').text("Connecting..."); - $('#setup-qr').html(''); - axolotl.protocol.createIdentityKeyRecvSocket().then(function(cryptoInfo) { - var qrCode = new QRCode(document.getElementById('setup-qr')); - - var socket = textsecure.api.getTempWebsocket(); - new WebSocketResource(socket, function(request) { - if (request.path == "/v1/address" && request.verb == "PUT") { - var proto = textsecure.protobuf.ProvisioningUuid.decode(request.body); - qrCode.makeCode('tsdevice:/' + - '?uuid=' + proto.uuid + - '&pub_key=' + encodeURIComponent(btoa(getString(cryptoInfo.pubKey)))); - $('img').removeAttr('style'); - $('#multi-device .status').text("Use your phone to scan the QR code."); - request.respond(200, 'OK'); - } else if (request.path == "/v1/message" && request.verb == "PUT") { - $('#init-setup').hide(); - $('#verify1done').text(''); - $('#verify2done').text(''); - $('#verify3done').text(''); - $('#verify4done').text(''); - $('#verify5done').text(''); - $('#verify').show().addClass('in'); - - textsecure.registerSecondDevice(request.body, cryptoInfo, function(step) { - switch(step) { - case 1: - $('#verify1done').text('done'); - break; - case 2: - $('#verify2done').text('done'); - break; - case 3: - $('#verify3done').text('done'); - break; - case 4: - //XXX: User needs to verify number before we continue - $('#verify4done').text('done'); - //$('#complete-number').text(parsedNumber); - textsecure.registration.done(); - //case 5: //TODO: Do sync to get 5! - $('#verify').hide(); - $('#setup-complete').show().addClass('in'); - textsecure.registration.done(); - } - }); - } else - console.log(request.path); - }); + } else + console.log(request.path); }); }); } diff --git a/js/register.js b/js/register.js new file mode 100644 index 00000000..5ff9dd50 --- /dev/null +++ b/js/register.js @@ -0,0 +1,122 @@ +/* vim: ts=4:sw=4 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +;(function() { + 'use strict'; + + function log(s) { + console.log(s); + $('#status').text(s); + } + + function validateCode() { + var verificationCode = $('#code').val().replace(/\D/g, ''); + if (verificationCode.length == 6) { + return verificationCode; + } + } + + function displayError(error) { + $('#error').hide().text(error).addClass('in').fadeIn(); + } + + var phoneView = new Whisper.PhoneInputView({el: $('#phone-number-input')}); + phoneView.$el.find('input.number').intlTelInput(); + + var number = textsecure.storage.getUnencrypted('number_id'); + if (number) { + $('input.number').val(number.split('.')[0]); + } + + $('input.number').on('validation', function() { + if ($('#number-container').hasClass('valid')) { + $('#request-sms, #request-voice').removeAttr('disabled'); + } else { + $('#request-sms, #request-voice').prop('disabled', 'disabled'); + } + }); + + $('#code').on('change', function() { + if (!validateCode()) + $('#code').addClass('invalid'); + else + $('#code').removeClass('invalid'); + }); + + $('#request-voice').click(function() { + $('#error').hide(); + var number = phoneView.validateNumber(); + if (number) { + textsecure.api.requestVerificationVoice(number).catch(displayError); + $('#step2').addClass('in').fadeIn(); + } else { + $('#number-container').addClass('invalid'); + } + }); + + $('#request-sms').click(function() { + $('#error').hide(); + var number = phoneView.validateNumber(); + if (number) { + textsecure.api.requestVerificationSMS(number).catch(displayError); + $('#step2').addClass('in').fadeIn(); + } else { + $('#number-container').addClass('invalid'); + } + }); + + $('#form').submit(function(e) { + e.preventDefault(); + log('registering'); + var number = $('input.number').val(); + var verificationCode = $('#code').val(); + var signalingKey = textsecure.crypto.getRandomBytes(32 + 20); + + var password = btoa(getString(textsecure.crypto.getRandomBytes(16))); + password = password.substring(0, password.length - 2); + + var registrationId = new Uint16Array(textsecure.crypto.getRandomBytes(2))[0]; + registrationId = registrationId & 0x3fff; + + log('clearing data'); + localStorage.clear(); + + localStorage.setItem('first_install_ran', 1); + textsecure.storage.putUnencrypted('registrationId', registrationId); + textsecure.storage.putEncrypted('signaling_key', signalingKey); + textsecure.storage.putEncrypted('password', password); + textsecure.storage.putUnencrypted('number_id', number + '.1'); + textsecure.storage.putUnencrypted('regionCode', libphonenumber.util.getRegionCodeForNumber(number)); + + log('verifying code'); + return textsecure.api.confirmCode( + number, verificationCode, password, signalingKey, registrationId, true + ).then(function() { + log('generating keys'); + return axolotl.protocol.generateKeys().then(function(keys) { + log('uploading keys'); + return textsecure.api.registerKeys(keys).then(function() { + textsecure.registration.done(); + log('done'); + chrome.runtime.reload(); + }); + }); + }).catch(function(e) { + log(e); + }); + }); + +})(); diff --git a/options.html b/options.html index d394801d..e0ba36c7 100644 --- a/options.html +++ b/options.html @@ -19,79 +19,82 @@ - -
-

TextSecure

-
-
-
-

I'm new to TextSecure

-
-
-

I have TextSecure installed on my phone.

-
-
-
-
-

-
-
-

-
-
-
-
-
- -
-
-
-
-
-
-
-
-
-
-
- - -
-
-
- - -
-
+
+
+
+
+
+ +
+
+

Welcome to Signal for Chrome

+

Private messaging from your web browser.

+
+
+
+
+
+
+
+

Step 1

+

Download the Mobile App

+
+
+

Step 2

+

Pair Your Device

+
+
+
+
+

+ To get started, download the TextSecure app for your Android phone. + Once you've installed it, proceed to Step 2. +

+

+
+

+ Using a QR code scanning app on your phone, scan the QR code below, then + open the link to pair your device. +

+
+
+
+
+
+ + + +
+
+
+
+
+
-
-
-
-
-
Receiving identity key...
-
Verifying number and setup code...
-
Generating keys...
-
Registering...
-
Syncing with existing devices...
+
+
+
+
+
+
+ +
+
+

Signal for Chrome

+

Private messaging from your web browser.

+
-
-
+ +

You are registered on TextSecure with number

- diff --git a/register.html b/register.html new file mode 100644 index 00000000..8e65a2f2 --- /dev/null +++ b/register.html @@ -0,0 +1,79 @@ + + + + + + Re-register TextSecure + + + + + + +
+
+
+
+ +
+
+

Create your Signal Account

+

Private messaging from your web browser.

+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+ + +
+
+

+ + +
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + diff --git a/stylesheets/options.css b/stylesheets/options.css index 44f1c165..cea87b85 100644 --- a/stylesheets/options.css +++ b/stylesheets/options.css @@ -13,51 +13,74 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ +@font-face { + font-family: 'Roboto-Light'; + src: url("/fonts/Roboto-Light.ttf") format("truetype"); } +@font-face { + font-family: 'Roboto'; + src: url("/fonts/Roboto-Regular.ttf") format("truetype"); } +@font-face { + font-family: 'Roboto'; + src: url("/fonts/Roboto-Italic.ttf") format("truetype"); + font-style: italic; } +@font-face { + font-family: 'Roboto'; + src: url("/fonts/Roboto-Bold.ttf") format("truetype"); + font-weight: bold; } +body { + font-family: Roboto, "Helvetica Neue", Arial, Helvetica, sans-serif; } -* { - font-family: Ubuntu, Segoe, 'Lucidia Grande', sans-serif; -} - -.paper { - background-color: #fafafa; - @include box-shadow(5px 0 5px -2px #ddd, -5px 0 5px -2px #ddd); -} +header { + background: #2a92e7; + color: white; + padding-bottom: 2em; + margin-bottom: 2em; } +.container { + min-width: 650px; } h1 { font-size: 30pt; font-weight: normal; - padding-bottom: 10px; -} + padding-bottom: 10px; } -h2 { - font-size: 12pt; - font-weight: normal; -} - -.left-column { - float: left; - width: 45%; -} - -.right-column { - float: right; - width: 50%; -} - -.hidden { - display: none; -} +.tagline { + font-style: italic; } #textsecure-icon { - width: 50px; - height: 50px; -} + float: left; + margin-top: 20px; + max-width: 100%; } -#setup-qr { - max-width: 256px; - margin-top: 1em; -} +#step1, #step2 { + color: #2a92e7; + font-weight: bold; + font-size: small; + text-transform: uppercase; } + +h3.step { + margin-top: 0; + font-weight: bold; } + +.cta { + text-align: center; + border: 2px solid #f3f3f3; + border-radius: 10px; + padding: 1em 0; + min-height: 293px; } + +.help { + border-top: 2px solid #f3f3f3; + padding: 1.5em 0.1em; } + +.install { + display: inline-block; + margin-top: 90px; } + +#qr { + display: inline-block; } + #qr canvas { + display: none; } #verifyCode, #code, @@ -65,35 +88,28 @@ h2 { box-sizing: border-box; width: 100%; display: block; - margin-bottom: 0.5em; -} + margin-bottom: 0.5em; } #request-voice, #request-sms { - box-sizing: border-box; -} + box-sizing: border-box; } + #request-sms { width: 57%; - float: right; -} + float: right; } + #request-voice { width: 40%; - float: left; -} - -.regionCode { - width: 100%; - margin-bottom: 1em; -} + float: left; } .number-container { position: relative; - margin-bottom: 0.5em; -} + margin-bottom: 0.5em; } + .number-container .intl-tel-input, .number-container .number { - width: 100%; -} + width: 100%; } + .number-container::after { visibility: hidden; content: ' '; @@ -107,47 +123,37 @@ h2 { top: 0; left: 100%; margin: 3px 8px; - text-align: center; -} + text-align: center; } + .number-container.valid::after { visibility: visible; content: '✓'; background-color: #0f9d58; - color: #ffffff; -} + color: #ffffff; } + .number-container.invalid::after { visibility: visible; content: '!'; background-color: #f44336; - color: #ffffff; -} + color: #ffffff; } #error { color: white; font-weight: bold; padding: 0.5em; - text-align: center; -} -#error { background-color: #f44336; } + text-align: center; } + +#error { + background-color: #f44336; } + #error:before { content: '\26a0'; - padding-right: 0.5em; -} + padding-right: 0.5em; } + .narrow { box-sizing: border-box; - margin: 0 auto 5px; width: 275px; - max-width: 100%; -} - -#single-device form { - margin: 2em 0; -} - -input.form-control, select.form-control { - border-radius: 0; -} + max-width: 100%; } ul.country-list { - min-width: 197px !important; -} + min-width: 197px !important; } diff --git a/stylesheets/options.scss b/stylesheets/options.scss new file mode 100644 index 00000000..20c775fe --- /dev/null +++ b/stylesheets/options.scss @@ -0,0 +1,164 @@ +/* + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +@import 'variables'; + +body { + font-family: $roboto; +} + +header { + background: $blue; + color: white; + padding-bottom: 2em; + margin-bottom: 2em; +} + +.container { + min-width: 650px; +} + +h1 { + font-size: 30pt; + font-weight: normal; + padding-bottom: 10px; +} + +.tagline { + font-style: italic; +} + +#textsecure-icon { + float: left; + margin-top: 20px; + max-width: 100%; +} + +#step1, #step2 { + color: $blue; + font-weight: bold; + font-size: small; + text-transform: uppercase; +} + +h3.step { + margin-top: 0; + font-weight: bold; +} + +.cta { + text-align: center; + border: 2px solid $grey_l; + border-radius: 10px; + padding: 1em 0; + min-height: 293px; +} + +.help { + border-top: 2px solid $grey_l; + padding: 1.5em 0.1em; +} + +.install { + display: inline-block; + margin-top: 90px; +} + +#qr { + display: inline-block; + + canvas { + display: none; + } +} + +#verifyCode, +#code, +#number { + box-sizing: border-box; + width: 100%; + display: block; + margin-bottom: 0.5em; +} + +#request-voice, +#request-sms { + box-sizing: border-box; +} +#request-sms { + width: 57%; + float: right; +} +#request-voice { + width: 40%; + float: left; +} + +.number-container { + position: relative; + margin-bottom: 0.5em; +} +.number-container .intl-tel-input, +.number-container .number { + width: 100%; +} +.number-container::after { + visibility: hidden; + content: ' '; + display: inline-block; + border-radius: 1.5em; + width: 1.5em; + height: 1.5em; + line-height: 1.5em; + color: #ffffff; + position: absolute; + top: 0; + left: 100%; + margin: 3px 8px; + text-align: center; +} +.number-container.valid::after { + visibility: visible; + content: '✓'; + background-color: #0f9d58; + color: #ffffff; +} +.number-container.invalid::after { + visibility: visible; + content: '!'; + background-color: #f44336; + color: #ffffff; +} + +#error { + color: white; + font-weight: bold; + padding: 0.5em; + text-align: center; +} +#error { background-color: #f44336; } +#error:before { + content: '\26a0'; + padding-right: 0.5em; +} +.narrow { + box-sizing: border-box; + width: 275px; + max-width: 100%; +} + +ul.country-list { + min-width: 197px !important; +}