Skip to content
This repository was archived by the owner on Jan 1, 2026. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion src/js/controllers/tab-send.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'
/* global angular */
angular.module('canoeApp.controllers').controller('tabSendController', function ($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, addressbookService, profileService, lodash, $state, incomingData, popupService, platformInfo, gettextCatalog, scannerService, externalLinkService) {
angular.module('canoeApp.controllers').controller('tabSendController', function ($scope, $rootScope, $log, $timeout, $ionicScrollDelegate, $ionicPopover, addressbookService, profileService, lodash, $state, incomingData, popupService, platformInfo, gettextCatalog, scannerService, externalLinkService, opencapService) {
var originalList
var completeContacts
var CONTACTS_SHOW_LIMIT
Expand Down Expand Up @@ -101,7 +101,38 @@ angular.module('canoeApp.controllers').controller('tabSendController', function
}
}

$scope.closePopover = function() {
$scope.popover.hide();
};

$scope.$on('$destroy', function() {
$scope.popover.remove();
});

$scope.confirmOpenapAddress = function(address){
$scope.popover.remove();
$scope.findContact(address);
}

$scope.resolveOpencapAlias = function(alias){
opencapService.getAddress(alias)
.then(result => {
$scope.opencapAddress = result.address;
$scope.opencapDnssec = result.dnssec;
$ionicPopover.fromTemplateUrl('templates/popoverAliasSend.html', {scope: $scope})
.then(function(popover) {
$scope.popover = popover;
popover.show(angular.element(document.querySelector('#search-input')))
});
})
.catch(status => {
// do nothing because they may have been typing
});
}

$scope.findContact = function (search) {
$scope.resolveOpencapAlias(search);

// If redir returns true it matched something and
// will already have moved us to amount.
incomingData.redir(search, $scope.acc.id, function (err, code) {
Expand Down
194 changes: 194 additions & 0 deletions src/js/services/opencapService.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
'use strict';

(function() {
angular.module('canoeApp.services').factory('opencapService', function($q, $http) {
function getAddress(alias) {
let aliasData = validateAlias(alias);
if (aliasData.username === '' || aliasData.domain === '') {
return $q((resolve, reject) => {
return reject('Invalid OpenCAP alias');
});
}

let deferred = $q.defer();
$http
.get(`https://dns.google.com/resolve?name=_opencap._tcp.${aliasData.domain}&type=SRV`)
.then(function(response) {
deferred.resolve(
parseSRV(response.data)
.then(data => getAddressReqeust(alias, data.host, data.dnssec))
.catch(function(error) {
return $q((resolve, reject) => {
reject(error);
});
})
);
})
.catch(function(response) {
deferred.reject("Couldn't find srv record for the provided domain");
});
return deferred.promise;
}

var parseSRV = function(respData) {
return $q((resolve, reject) => {
let dnssec = respData.AD;

if (typeof respData.Answer === 'undefined') {
return reject('Error contacting google dns server, no srv data');
}
if (respData.Answer.length < 1) {
return reject('Error contacting google dns server, not enough srv data');
}

let record = respData.Answer[0].data.split(' ');
if (record.length != 4) {
return reject('Error contacting google dns server, improper srv data');
}

if (record[3].slice(-1) == '.') {
record[3] = record[3].substring(0, record[3].length - 1);
}

return resolve({ host: record[3], dnssec });
});
};

var getAddressReqeust = function(alias, host, dnssec) {
let deferred = $q.defer();
$http
.get(`https://${host}/v1/addresses?alias=${alias}&address_type=300`)
.then(function(response) {
deferred.resolve(parseAddress(response.data, dnssec).then());
})
.catch(function(response) {
deferred.reject('Address not found for the specified alias');
});
return deferred.promise;
};

var parseAddress = function(respData, dnssec) {
return $q((resolve, reject) => {
if (respData.address_type === 'undefined') {
return reject('Error contacting opencap server, no response');
}
if (respData.address === 'undefined') {
return reject('Error contacting opencap server, no response');
}
return resolve({ address: respData.address, dnssec });
});
};

function updateAddress(alias, password, address) {
let aliasData = validateAlias(alias);
if (aliasData.username === '' || aliasData.domain === '') {
return $q((resolve, reject) => {
return reject('Invalid OpenCAP alias');
});
}

let deferred = $q.defer();
$http
.get(`https://dns.google.com/resolve?name=_opencap._tcp.${aliasData.domain}&type=SRV`)
.then(function(response) {
deferred.resolve(
parseSRV(response.data)
.then(data =>
loginRequest(alias, password, data.host)
.then(jwt => updateAddressRequest(alias, address, data.host, jwt))
.catch(function(error) {
return $q((resolve, reject) => {
reject(error);
});
})
)
.catch(function(error) {
return $q((resolve, reject) => {
reject(error);
});
})
);
})
.catch(function(response) {
deferred.reject("Couldn't find srv record for the provided domain");
});
return deferred.promise;
}

function updateAddressRequest(alias, address, host, jwt) {
let deferred = $q.defer();
let headers = new Headers({
'Content-Type': 'application/json',
Authorization: `Bearer ${jwt}`,
});
let options = new RequestOptions({ headers: headers });
const nano_address_type = 300;
$http
.put(`https://${host}/v1/addresses`, { address_type: nano_address_type, address }, options)
.then(function(response) {
deferred.resolve('Alias was updated succesfully');
})
.catch(function(response) {
deferred.reject('Failed to update alias');
});
return deferred.promise;
}

function loginRequest(alias, password, host) {
let deferred = $q.defer();
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
$http
.post(`https://${host}/v1/auth`, { alias, password }, options)
.then(function(response) {
deferred.resolve(parseJWT(response.data).then());
})
.catch(function(response) {
deferred.reject('Invalid login credentials');
});
return deferred.promise;
}

var parseJWT = function(respData) {
return $q((resolve, reject) => {
if (respData.jwt === 'undefined') {
return reject('Error contacting opencap server, no response');
}
return resolve(respData.jwt);
});
};

function validateUsername(username) {
return /^[a-z0-9._-]{1,25}$/.test(username);
}

function validateDomain(username) {
return /^[a-z0-9.\-]+\.[a-z]{2,4}$/.test(username);
}

function validateAlias(alias) {
let splitAlias = alias.split('$');
if (splitAlias.length != 2) {
return { username: '', domain: '' };
}
let username = splitAlias[0];
let domain = splitAlias[1];

if (!validateUsername(username)) {
return { username: '', domain: '' };
}
if (!validateDomain(domain)) {
return { username: '', domain: '' };
}

return { username, domain };
}

var service = {
updateAddress,
getAddress,
};

return service;
});
})();
16 changes: 16 additions & 0 deletions www/templates/popoverAliasSend.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<ion-popover-view style="height: 0px">
<div class="card" style="text-align: center">
<div class="item item-heading">
<span translate>Address found!</span>
</div>
<div class="list">
<div class="item">
<span class="item">{{opencapAddress}}</span>
<button ng-click="confirmOpenapAddress(opencapAddress)" class="button button-standard button-primary" translate>Send</button>
<span ng-if="!opencapDnssec" style="color: red; " translate>
DNSSEC not used
</span>
</div>
</div>
</div>
</ion-popover-view>
2 changes: 1 addition & 1 deletion www/views/tab-send.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<div class="item input search-wrapper" ng-class="{'focus': searchFocus}">
<i class="icon icon-svg left abs-v-center"><img class="png" id="Nano_Symbol" src="img/nano-logo-white.png"></i>
<div class="item-icon-right">
<input type="text" class="search-input" placeholder="{{'Search or enter account number' | translate}}" ng-model="formData.search" ng-change="findContact(formData.search)" ng-model-onblur ng-focus="searchInFocus()" ng-blur="searchBlurred()">
<input id="search-input" type="text" class="search-input" placeholder="{{'Search or enter account number or alias' | translate}}" ng-model="formData.search" ng-change="findContact(formData.search)" ng-model-onblur ng-focus="searchInFocus()" ng-blur="searchBlurred()">
<i class="icon icon-svg qr abs-v-center separator-left" on-tap="openScanner()"><img src="img/scan-ico.svg"></i>
</div>
</div>
Expand Down