Commit f9f51b7b authored by Benoit Lavenier's avatar Benoit Lavenier

Allow WIF and EWIF format in login by file (or by QR code)

parent 16662afe
......@@ -422,7 +422,7 @@
"DATE" : "Date :",
"TYPE" : "Type :",
"SIZE": "Taille :",
"VALIDATING": "Validation...",
"VALIDATING": "Validation en cours...",
"HELP": "Format de fichier attendu : <b>.dunikey</b> (type PubSec). D'autres formats sont en cours de développement (EWIF, WIF)."
}
},
......@@ -518,6 +518,9 @@
"BTN_RESET": "Réinitialiser",
"DOWNLOAD_REVOKE": "Sauvegarder mon fichier de révocation",
"DOWNLOAD_REVOKE_HELP": "Disposer d'un fichier de révocation est important, par exemple en cas de perte de vos identifiants. Il vous permet de <b>sortir ce compte de la toile de confiance</b>, en redevenant ainsi un simple portefeuille.",
"GENERATE_KEYFILE": "Générer mon fichier de trousseau...",
"GENERATE_KEYFILE_HELP": "Génère un fichier permettant de vous authentifier sans saisir vos identifiants.<br/><b>Attention :</b> ce fichier contiendra votre clef secrète : il est donc très important de le mettre en lieu sûr !",
"KEYFILE_FILENAME": "trousseau-{{pubkey|formatPubkey}}-{{currency}}-{{format}}.dunikey",
"MEMBERSHIP_IN": "Transformer en compte membre...",
"MEMBERSHIP_IN_HELP": "Permet de <b>transformer</b> un compte simple portefeuille <b>en compte membre</b>, en envoyant une demande d'adhésion. Utile uniquement si vous n'avez pas déjà une autre compte membre.",
"SEND_IDENTITY": "Publier son identité...",
......@@ -556,7 +559,20 @@
"SAVE_ID": "Sauvegarder mes identifiants...",
"SAVE_ID_HELP": "Création d'un fichier de sauvegarde, pour <b>retrouver votre mot de passe</b> (et l'identifiant secret) <b>en cas de d'oubli</b>. Le fichier est <b>sécurisé</b> (chiffré) à l'aide de questions personnelles.",
"STRONG_LEVEL": "Fort <span class=\"hidden-xs \">(6 questions minimum)</span>",
"TITLE": "Compte et sécurité"
"TITLE": "Compte et sécurité",
"KEYFILE": {
"PUBSEC_FORMAT": "Format PubSec.",
"PUBSEC_FORMAT_HELP": "Ce format de fichier est compatible notamment avec Cesium et gannonce.",
"WIF_FORMAT": "Format WIF",
"WIF_FORMAT_HELP": "Ce format permet notamment la génération de portefeuilles papier (Paper Wallet).",
"EWIF_FORMAT": "Format EWIF",
"EWIF_FORMAT_HELP": "Ce format protège le trousseau par un mot de passe. Il permet notamment la génération de portefeuilles papier (Paper Wallet).",
"PASSWORD_POPUP": {
"TITLE": "Fichier de trousseau chiffré",
"HELP": "Veuillez indiquer le mot de passe:",
"PASSWORD_HELP": "Mot de passe"
}
}
},
"FILE_NAME": "{{currency}} - Relevé du compte {{pubkey|formatPubkey}} au {{currentTime|formatDateForFile}}.csv",
"HEADERS": {
......@@ -582,9 +598,12 @@
}
},
"ERROR": {
"UNKNOWN_URI_FORMAT": "Format d'URI inconnu",
"PUBKEY_INVALID_CHECKSUM": "Clé publique invalide (bad checksum).",
"POPUP_TITLE": "Erreur",
"UNKNOWN_ERROR": "Erreur inconnue",
"CRYPTO_UNKNOWN_ERROR": "Votre navigateur ne semble pas compatible avec les fonctionnalités de cryptographie.",
"DOWNLOAD_KEYFILE_FAILED": "Echec de le génération du fichier de trousseau.",
"EQUALS_TO_PSEUDO": "Doit être différent du pseudonyme",
"EQUALS_TO_SALT": "Doit être différent de l'identifiant secret",
"FIELD_REQUIRED": "Champ obligatoire.",
......@@ -634,6 +653,7 @@
"INVALID_USER_ID": "Le pseudonyme ne doit contenir ni espace ni caractère spécial ou accentué.",
"INVALID_COMMENT": "Le champ 'référence' ne doit pas contenir de caractères accentués.",
"INVALID_PUBKEY": "La clé publique n'a pas le format attendu.",
"INVALID_PUBKEY_CHECKSUM": "Somme de contrôle invalide.",
"IDENTITY_REVOKED": "Cette identité <b>a été révoquée {{revocationTime|formatFromNow}}</b> ({{revocationTime|formatDate}}). Elle ne peut plus devenir membre.",
"IDENTITY_PENDING_REVOCATION": "La <b>révocation de cette identité</b> a été demandée et est en attente de traitement. La certification est donc désactivée.",
"IDENTITY_INVALID_BLOCK_HASH": "Cette demande d'adhésion n'est plus valide (car elle référence un bloc que les nœuds du réseau ont annulé) : cette personne doit renouveler sa demande d'adhésion <b>avant</b> d'être certifiée.",
......
......@@ -14,6 +14,11 @@
<link rel="stylesheet" type="text/css" href="css/angular-image-crop.css">
<!--removeIf(device)-->
<link rel="stylesheet" type="text/css" href="css/style-no-device.css">
<meta property="og:title" content="Cesium" />
<meta property="og:description" content="Cesium is a Wallet for the Ğ1 currency." />
<meta property="og:type" content="website" />
<meta property="og:image" content="./img/logo_200px.png" />
<!--endRemoveIf(device)-->
<!--removeIf(no-plugin)-->
......@@ -50,6 +55,7 @@
<script src="js/vendor/socket-io.js"></script>
<script src="js/vendor/underscore.js"></script>
<script src="js/vendor/qrcode.min.js"></script>
<script src="js/vendor/aes-js.js"></script>
<!--removeIf(ubuntu)--> <!-- FIXME: issue #463 -->
<script src="js/vendor/Chart.js"></script>
<!--endRemoveIf(ubuntu)-->
......
......@@ -64,7 +64,7 @@ function PluginExtensionPointController($scope, PluginService) {
* Abstract controller (inherited by other controllers)
*/
function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $timeout,
$ionicHistory, $controller, $window, csPlatform,
$ionicHistory, $controller, $window, csPlatform, CryptoUtils,
UIUtils, BMA, csWallet, Device, Modals, csConfig, csHttp
) {
'ngInject';
......@@ -86,29 +86,74 @@ function AppController($scope, $rootScope, $state, $ionicSideMenuDelegate, $q, $
// Device Methods
////////////////////////////////////////
function parseWif(data) {
return CryptoUtils.readWif(data, {
password: function() {
return Modals.showPassword({
title: 'ACCOUNT.SECURITY.KEYFILE.PASSWORD_POPUP.TITLE',
subTitle: 'ACCOUNT.SECURITY.KEYFILE.PASSWORD_POPUP.HELP'
})
.then(function(password) {
UIUtils.loading.show();
return password;
});
}
})
.catch(function(err) {
if (err && err == 'CANCELLED') return;
if (err && err == 'BAD_PASSWORD') return parseWif(); // recursive call
console.error("[app] Unable to parse as WIF or EWIF format: " + (err && err.message || err));
throw err; // rethrow
});
}
$scope.scanQrCodeAndGo = function() {
if (!Device.barcode.enable) {
return;
}
Device.barcode.scan()
.then(function(uri) {
if (!uri) {
return;
}
BMA.uri.parse(uri)
.then(function(result){
// If pubkey
if (result && result.pubkey) {
$state.go('app.wot_identity', {
pubkey: result.pubkey,
node: result.host ? result.host: null}
);
}
else {
UIUtils.alert.error(result, 'ERROR.SCAN_UNKNOWN_FORMAT');
}
.then(function(data) {
if (!data) return;
// Parse as an URI
BMA.uri.parse(data)
.then(function(res){
if (!res || res.pubkey) throw {message: 'ERROR.SCAN_UNKNOWN_FORMAT'};
// If pubkey: open the identity
return $state.go('app.wot_identity', {
pubkey: res.pubkey,
node: res.host ? res.host: null}
);
})
.catch(UIUtils.onError('ERROR.SCAN_UNKNOWN_FORMAT'));
// Not an URI: try WIF or EWIF format
.catch(function(err) {
console.debug(err && err.message || err);
// Try to read as WIF format
return parseWif(data)
.then(function(keypair) {
if (!keypair || !keypair.signPk || !keypair.signSk) throw {message: 'ERROR.SCAN_UNKNOWN_FORMAT'};
var pubkey = CryptoUtils.base58.encode(keypair.signPk);
return csWallet.login({
forceAuth: true,
minData: false,
authData: {
pubkey: pubkey,
keypair: keypair
}
})
.then(function () {
return $state.go('app.view_wallet');
});
// TODO: if WIF, force redirection to a transfer modal, using a temporary wallet:
// var wallet = csWallet.instance('WIF');
// return wallet.login(...)
})
// Unknown format (nor URI, nor WIF/EWIF)
.catch(UIUtils.onError('ERROR.SCAN_UNKNOWN_FORMAT'));
});
})
.catch(UIUtils.onError('ERROR.SCAN_FAILED'));
};
......
......@@ -7,7 +7,8 @@ angular.module('cesium.login.controllers', ['cesium.services'])
;
function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils, UIUtils, BMA, Modals, csSettings, Device, parameters) {
function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
UIUtils, BMA, Modals, csSettings, Device, parameters) {
'ngInject';
parameters = parameters || {};
......@@ -18,7 +19,7 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
$scope.showPubkey = false;
$scope.showComputePubkeyButton = false;
$scope.autoComputePubkey = false;
$scope.pubkeyPattern = '^' + BMA.constants.regexp.PUBKEY + '$';
$scope.pubkeyPattern = '^(:?{0}|{1})$'.format(BMA.constants.regexp.PUBKEY, BMA.constants.regexp.PUBKEY_WITH_CHECKSUM);
$scope.isAuth = parameters.auth;
$scope.showMethods = angular.isDefined(parameters.showMethods) ? parameters.showMethods : true;
......@@ -116,8 +117,13 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
// If checkbox keep auth checked: set idle time to session
keepAuthIdle = ($scope.formData.keepAuth && csSettings.constants.KEEP_AUTH_IDLE_SESSION) || keepAuthIdle;
UIUtils.loading.show();
promise = CryptoUtils.readKeyFile($scope.formData.file, $scope.isAuth||$scope.formData.keepAuth/*withSecret*/)
promise =
UIUtils.loading.show()
.then(function() {
return $scope.readKeyFile($scope.formData.file, {
withSecret: ($scope.isAuth || $scope.formData.keepAuth)
});
})
.then(function(keypair) {
if (!keypair) return UIUtils.loading.hide(10);
var pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
......@@ -128,6 +134,10 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
return UIUtils.loading.hide(10);
}
// TODO: if WIF, force redirection to a transfer modal, using a temporary wallet:
// var wallet = csWallet.instance('WIF');
// return wallet.login(...)
$scope.pubkeyError = false;
return {
......@@ -141,9 +151,26 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
// Pubkey
else if (method === 'PUBKEY') {
if (!$scope.formData.pubkey) return;
promise = $q.when({
pubkey: $scope.formData.pubkey
});
var matches = BMA.regexp.PUBKEY_WITH_CHECKSUM.exec($scope.formData.pubkey);
// Check checksum
if (matches) {
var pubkey = matches[1];
var checksum = matches[2];
var expectedChecksum = CryptoUtils.pkChecksum(pubkey);
if (checksum != expectedChecksum) {
$scope.form.pubkey.$error = {checksum: true};
}
else {
promise = $q.when({
pubkey: pubkey
});
}
}
else {
promise = $q.when({
pubkey: $scope.formData.pubkey.trim()
});
}
}
if (!promise) {
......@@ -294,6 +321,32 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
$scope.onScryptFormChanged();
};
$scope.readKeyFile = function(file, options) {
options = options || {};
options.password = options.password || $scope.formData.file.password || function() {
$scope.formData.file.password = undefined;
return Modals.showPassword({
title: 'ACCOUNT.SECURITY.KEYFILE.PASSWORD_POPUP.TITLE',
subTitle: 'ACCOUNT.SECURITY.KEYFILE.PASSWORD_POPUP.HELP'
})
.then(function (password) {
// Remember password (for validation)
$scope.formData.file.password = password;
return password;
});
};
return CryptoUtils.readKeyFile($scope.formData.file, options)
.catch(function(err) {
if (err && err == 'CANCELLED') return;
if (err && err == 'BAD_PASSWORD') {
return $scope.readKeyFile($scope.formData.file, options); // Loop (ask the password again)
}
throw err;
});
};
$scope.fileChanged = function(event) {
$scope.validatingFile = true;
$scope.formData.file = event && event.target && event.target.files && event.target.files.length && event.target.files[0];
......@@ -306,7 +359,7 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
console.debug("[login] key file changed: ", $scope.formData.file);
$scope.validatingFile = true;
return CryptoUtils.readKeyFile($scope.formData.file, false/*withSecret*/)
return $scope.readKeyFile($scope.formData.file, {withSecret: false, password: $scope.formData.file.password})
.then(function(keypair) {
if (!keypair || !keypair.signPk) {
$scope.formData.file.valid = false;
......@@ -320,6 +373,10 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
})
.catch(function(err) {
if (err && err == 'CANCELLED') {
$scope.removeKeyFile();
return;
}
$scope.validatingFile = false;
$scope.formData.file.valid = false;
$scope.formData.file.pubkey = undefined;
......@@ -329,9 +386,8 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
};
/**
* Recover Id
* On file drop
*/
$scope.onKeyFileDrop = function(file) {
if (!file || !file.fileData) return;
......@@ -340,25 +396,28 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
size: file.fileData.size,
content: file.fileContent
};
return CryptoUtils.readKeyFile($scope.formData.file, false/*withSecret*/)
.then(function(keypair) {
if (!keypair || !keypair.signPk) {
$scope.validatingFile = true;
$timeout(function() {
return $scope.readKeyFile($scope.formData.file, {withSecret: false})
.then(function (keypair) {
if (!keypair || !keypair.signPk) {
$scope.formData.file.valid = false;
$scope.formData.file.pubkey = undefined;
}
else {
$scope.formData.file.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
$scope.formData.file.valid = !$scope.expectedPubkey || $scope.expectedPubkey == $scope.formData.file.pubkey;
$scope.validatingFile = false;
}
})
.catch(function (err) {
$scope.validatingFile = false;
$scope.formData.file.valid = false;
$scope.formData.file.pubkey = undefined;
}
else {
$scope.formData.file.pubkey = CryptoUtils.util.encode_base58(keypair.signPk);
$scope.formData.file.valid = !$scope.expectedPubkey || $scope.expectedPubkey == $scope.formData.file.pubkey;
$scope.validatingFile = false;
}
})
.catch(function(err) {
$scope.validatingFile = false;
$scope.formData.file.valid = false;
$scope.formData.file.pubkey = undefined;
UIUtils.onError('ERROR.AUTH_FILE_ERROR')(err);
});
UIUtils.onError('ERROR.AUTH_FILE_ERROR')(err);
});
});
};
$scope.removeKeyFile = function() {
......@@ -367,8 +426,8 @@ function LoginModalController($scope, $timeout, $q, $ionicPopover, CryptoUtils,
/* -- modals -- */
$scope.showWotLookupModal = function() {
return Modals.showWotLookup()
$scope.showWotLookupModal = function(searchText) {
return Modals.showWotLookup({q: searchText})
.then(function(res){
if (res && res.pubkey) {
$scope.formData.pubkey = res.pubkey;
......
......@@ -44,7 +44,7 @@ angular.module('cesium.transfer.controllers', ['cesium.services', 'cesium.curren
.controller('TransferModalCtrl', TransferModalController)
;
function TransferController($scope, $controller, Device, UIUtils, csWot, csWallet) {
function TransferController($scope, $controller, UIUtils, csWot, csWallet) {
'ngInject';
// Initialize the super class and extend it.
......
......@@ -865,20 +865,35 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate){
$scope.doNext = function(formName) {
if (!formName) {
formName = $scope.slides.slider.activeIndex === 1 && $scope.option == "saveID" ? 'questionsForm' :
($scope.slides.slider.activeIndex === 2 && $scope.option === "recoverID" ? 'recoverForm' :
($scope.slides.slider.activeIndex === 2 && $scope.option === "saveID" ? 'answersForm' : formName));
switch ($scope.slides.slider.activeIndex) {
case 1:
switch ($scope.option) {
case "saveID":
formName = "questionsForm";
break;
case "recoverID":
if ($scope.isValidFile) {
$scope.slideNext();
$scope.hasContent = false;
$scope.fileData = '';
if ($scope.slides.slider.activeIndex === 1 && $scope.option === "recoverID") {
if ($scope.isValidFile) {
$scope.slideNext();
$scope.hasContent = false;
$scope.fileData = '';
}
else {
UIUtils.alert.error("ERROR.NOT_VALID_SAVE_ID_FILE", "ERROR.LOAD_FILE_FAILED");
}
}
else {
UIUtils.alert.error("ERROR.NOT_VALID_SAVE_ID_FILE", "ERROR.LOAD_FILE_FAILED");
}
break;
}
break;
case 2:
switch ($scope.option) {
case "recoverID":
formName = "recoverForm";
break;
case "saveID":
formName = "answersForm";
break;
}
}
}
......@@ -887,14 +902,15 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate){
if (!$scope[formName].$valid) {
return;
}
if(formName === 'recoverForm'){
$scope.recoverId();
}
else if(formName === 'answersForm'){
$scope.downloadSaveIDFile();
}
else {
$scope.slideNext();
switch (formName) {
case "recoverForm":
$scope.recoverId();
break;
case "answersForm":
$scope.downloadSaveIDFile();
break;
default:
$scope.slideNext();
}
}
};
......@@ -1179,6 +1195,30 @@ function WalletSecurityModalController($scope, UIUtils, csWallet, $translate){
return $scope.closeModal('membershipIn');
};
/**
* Generate keyfile
*/
$scope.downloadKeyFile = function (format) {
// Force re-authentication
return csWallet.auth({forceAuth: true})
// Download file
.then(function() {
return csWallet.downloadKeyFile(format);
})
.then(function() {
UIUtils.loading.hide();
return $scope.closeModal();
})
.catch(function(err){
if (err && err == 'CANCELLED') return;
UIUtils.onError('ERROR.DOWNLOAD_KEYFILE_FAILED')(err);
})
;
};
}
......@@ -170,16 +170,17 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
$scope.showResultLabel = true;
$scope.parameters = {}; // override in the modal controller
$scope.$on('$ionicView.enter', function(e, state) {
$scope.enter = function(e, state) {
if (!$scope.entered) {
if (state.stateParams && state.stateParams.q) { // Query parameter
$scope.search.text = state.stateParams.q;
var params = angular.merge({}, $scope.parameters, state && state.stateParams);
if (params && params.q) { // Query parameter
$scope.search.text = params.q;
$timeout(function() {
$scope.doSearch();
}, 100);
}
else if (state.stateParams && state.stateParams.hash) { // hash tag parameter
$scope.search.text = '#' + state.stateParams.hash;
else if (params && params.hash) { // hash tag parameter
$scope.search.text = '#' + params.hash;
$timeout(function() {
$scope.doSearch();
}, 100);
......@@ -187,14 +188,14 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
else {
$timeout(function() {
// Init phase
if (csCurrency.data.initPhase && !state.stateParams.type) {
if (csCurrency.data.initPhase && !params.type) {
$scope.doGetPending(0, undefined, true/*skipLocationUpdate*/);
}
// get new comers
else if (state.stateParams.type == 'newcomers' || (!csConfig.initPhase && !state.stateParams.type)) {
else if (params.type == 'newcomers' || (!csConfig.initPhase && !params.type)) {
$scope.doGetNewcomers(0, undefined, true/*skipLocationUpdate*/);
}
else if (state.stateParams.type == 'pending') {
else if (params.type == 'pending') {
$scope.doGetPending(0, undefined, true/*skipLocationUpdate*/);
}
......@@ -218,7 +219,8 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
$scope.motion.show({selector: '.lookupForm .list .item', ink: true});
}
}
});
};
$scope.$on('$ionicView.enter', $scope.enter);
$scope.resetWotSearch = function() {
$scope.search = {
......@@ -282,8 +284,11 @@ function WotLookupController($scope, $state, $q, $timeout, $focus, $location, $i
if ($scope.search.type != 'text') return; // could have change
if ($scope.search.text.trim() !== text) return; // search text has changed before received response
if ((!idties || !idties.length) && BMA.regexp.PUBKEY.test(text)) {
$scope.doDisplayResult([{pubkey: text}]);
if ((!idties || !idties.length) && (BMA.regexp.PUBKEY.test(text) || BMA.regexp.PUBKEY_WITH_CHECKSUM.test(text))) {
return BMA.uri.parse(text)
.then(function(data) {
$scope.doDisplayResult([data]);
});
}
else {
$scope.doDisplayResult(idties);
......@@ -578,6 +583,17 @@ function WotLookupModalController($scope, $controller, $focus, parameters){
$scope.selection = parameters.selection;
}
var superEnter = $scope.enter;
$scope.enter = function(e) {
if ($scope.parameters && $scope.parameters.q) {
$scope.search.text=$scope.parameters.q;
if ($scope.parameters.q.trim().length > 2) {
superEnter(e); // call enter, that launch the search
}
}
};
$scope.$on('modal.shown', $scope.enter);
$scope.cancel = function(){
$scope.closeModal();
};
......
......@@ -2,11 +2,12 @@
angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.settings.services'])
.factory('BMA', function($q, $window, $rootScope, $timeout, Api, Device, csConfig, csSettings, csHttp) {
.factory('BMA', function($q, $window, $rootScope, $timeout, CryptoUtils, Api, Device, csConfig, csSettings, csHttp) {
'ngInject';
function BMA(host, port, useSsl, useCache) {
var pubkey = "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}";
var
// TX output conditions
SIG = "SIG\\(([0-9a-zA-Z]{43,44})\\)",
......@@ -22,11 +23,12 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
regexp = {
USER_ID: "[A-Za-z0-9_-]+",
CURRENCY: "[A-Za-z0-9_-]+",
PUBKEY: "[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44}",
PUBKEY: pubkey,
PUBKEY_WITH_CHECKSUM: "(" + pubkey +")" + ":([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{3})",
COMMENT: "[ a-zA-Z0-9-_:/;*\\[\\]()?!^\\+=@&~#{}|\\\\<>%.]*",
// duniter://[uid]:[pubkey]@[host]:[port]
URI_WITH_AT: "duniter://(?:([A-Za-z0-9_-]+):)?([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44})@([a-zA-Z0-9-.]+.[ a-zA-Z0-9-_:/;*?!^\\+=@&~#|<>%.]+)",
URI_WITH_PATH: "duniter://([a-zA-Z0-9-.]+.[a-zA-Z0-9-_:.]+)/([123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]{43,44})(?:/([A-Za-z0-9_-]+))?",
URI_WITH_AT: "duniter://(?:([A-Za-z0-9_-]+):)?("+pubkey+"@([a-zA-Z0-9-.]+.[ a-zA-Z0-9-_:/;*?!^\\+=@&~#|<>%.]+)",
URI_WITH_PATH: "duniter://([a-zA-Z0-9-.]+.[a-zA-Z0-9-_:.]+)/("+pubkey+")(?:/([A-Za-z0-9_-]+))?",
BMA_ENDPOINT: "BASIC_MERKLED_API" + REGEX_ENDPOINT_PARAMS,
BMAS_ENDPOINT: "BMAS" + REGEX_ENDPOINT_PARAMS,
WS2P_ENDPOINT: "WS2P ([a-f0-9]{8})"+ REGEX_ENDPOINT_PARAMS,
......@@ -349,6 +351,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
USER_ID: exact(regexp.USER_ID),
COMMENT: exact(regexp.COMMENT),
PUBKEY: exact(regexp.PUBKEY),
PUBKEY_WITH_CHECKSUM: exact(regexp.PUBKEY_WITH_CHECKSUM),
CURRENCY: exact(regexp.CURRENCY),
URI: exact(regexp.URI),
BMA_ENDPOINT: exact(regexp.BMA_ENDPOINT),
......@@ -722,15 +725,26 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
exports.uri.parse = function(uri) {
return $q(function(resolve, reject) {
var pubkey;
// If pubkey: not need to parse
if (exact(regexp.PUBKEY).test(uri)) {
resolve({
pubkey: uri
});
}
// If pubkey+checksum
else if (exact(regexp.PUBKEY_WITH_CHECKSUM).test(uri)) {
var matches = exports.regexp.PUBKEY_WITH_CHECKSUM.exec(uri);
pubkey = matches[1];
var checksum = matches[2];
var expectedChecksum = CryptoUtils.pkChecksum(pubkey);
if (checksum != expectedChecksum) throw {message: 'ERROR.PUBKEY_INVALID_CHECKSUM'};
resolve({
pubkey: pubkey
});
}
else if(uri.startsWith('duniter://')) {
var parser = csHttp.uri.parse(uri),
pubkey,
uid,
currency = parser.host.indexOf('.') === -1 ? parser.host : null,
host = parser.host.indexOf('.') !== -1 ? parser.host : null;
......@@ -805,7 +819,7 @@ angular.module('cesium.bma.services', ['ngApi', 'cesium.http.services', 'cesium.
}
}
else {
throw {message: 'Bad URI format: ' + uri};
reject( {message: 'ERROR.UNKNOWN_URI_FORMAT'});
}
})
......
This diff is collapsed.
angular.module('cesium.currency.services', ['ngApi', 'cesium.bma.services'])
.factory('csCurrency', function($rootScope, $q, $timeout, BMA, Api) {
.factory('csCurrency', function($rootScope, $q, $timeout, BMA, Api, csSettings) {
'ngInject';
function factory(id, BMA) {
......@@ -266,6 +266,17 @@ angular.module('cesium.currency.services', ['ngApi', 'cesium.bma.services'])
});
}
function getLastValidBlock() {
if (csSettings.data.wallet.txBlockReferenceCount > 0) {
return getCurrent(true)
.then(function(current) {
var number = current.number - csSettings.data.wallet.txBlockReferenceCount;
return (number > 0) ? BMA.blockchain.get(number) : current;
})
}
return getCurrent(true);
}
// TODO register new block event, to get new UD value
// Register extension points
......@@ -289,7 +300,8 @@ angular.module('cesium.currency.services', ['ngApi', 'cesium.bma.services'])
parameters: getDataField('parameters'),
currentUD: getDataField('currentUD'),
blockchain: {
current: getCurrent
current: getCurrent,
lastValid: getLastValidBlock
},
// api extension
api: api,
......
......@@ -87,16 +87,17 @@ angular.module('cesium.device.services', ['cesium.utils.services', 'cesium.setti
var deferred = $q.defer();
cordova.plugins.barcodeScanner.scan(
function(result) {
console.debug('[device] bar code result', result);
if (!result.cancelled) {
console.debug('[device] barcode scanner scan: ' + result.text);
deferred.resolve(result.text); // make sure to convert into String
}
else {
console.debug('[device] barcode scanner scan: CANCELLED');
deferred.resolve();
}
},
function(err) {
console.error('[device] Error while using barcode scanner -> ' + err);
console.error('[device] Error while using barcode scanner