You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
718 lines
26 KiB
718 lines
26 KiB
// twister_io.js |
|
// 2013 Miguel Freitas |
|
// |
|
// low-level twister i/o. |
|
// implements requests of dht resources. multiple pending requests to the same resource are joined. |
|
// cache results (profile, avatar, etc) in memory. |
|
// avatars are cached in localstored (expiration = 24 hours) |
|
|
|
// main json rpc method. receives callbacks for success and error |
|
function twisterRpc(method, params, resultFunc, resultArg, errorFunc, errorArg) { |
|
// removing hardcoded username from javascript: please use url http://user:pwd@localhost:28332 instead |
|
//var foo = new $.JsonRpcClient({ ajaxUrl: '/', username: 'user', password: 'pwd'}); |
|
var foo = new $.JsonRpcClient({ajaxUrl: window.location.pathname.replace(/[^\/]*$/, '')}); |
|
foo.call(method, params, |
|
function(ret) {resultFunc(resultArg, ret);}, |
|
function(ret) {errorFunc(errorArg, ret);} // FIXME why only if "(ret != null)"? |
|
); |
|
} |
|
|
|
// join multiple dhtgets to the same resources in this map |
|
var _dhtgetPendingMap = {}; |
|
|
|
var _pubkeyMap = {}; |
|
|
|
// number of dhtgets in progress (requests to the daemon) |
|
var _dhtgetsInProgress = 0; |
|
|
|
// keep _maxDhtgets smaller than the number of daemon/browser sockets |
|
// most browsers limit to 6 per domain (see http://www.browserscope.org/?category=network) |
|
var _maxDhtgets = 5; |
|
|
|
// requests not yet sent to the daemon due to _maxDhtgets limit |
|
var _queuedDhtgets = []; |
|
|
|
// private function to define a key in _dhtgetPendingMap |
|
function _dhtgetLocator(peerAlias, resource, multi) { |
|
return peerAlias + ';' + resource + ';' + multi; |
|
} |
|
|
|
function _dhtgetAddPending(locator, cbFunc, cbReq) { |
|
if (!_dhtgetPendingMap[locator]) { |
|
_dhtgetPendingMap[locator] = []; |
|
} |
|
_dhtgetPendingMap[locator].push({cbFunc: cbFunc, cbReq: cbReq}); |
|
} |
|
|
|
function _dhtgetProcessPending(locator, multi, ret) { |
|
if (_dhtgetPendingMap[locator]) { |
|
for (var i = 0; i < _dhtgetPendingMap[locator].length; i++) { |
|
var cbFunc = _dhtgetPendingMap[locator][i].cbFunc; |
|
var cbReq = _dhtgetPendingMap[locator][i].cbReq; |
|
|
|
if (multi === 'url') { |
|
cbFunc(cbReq, ret); // here is decodeshorturl case |
|
} else if (multi === 's') { |
|
if (typeof ret[0] !== 'undefined') { |
|
cbFunc(cbReq, ret[0].p.v, ret); |
|
} else { |
|
cbFunc(cbReq); |
|
} |
|
} else { |
|
var multiret = []; |
|
for (var j = 0; j < ret.length; j++) { |
|
multiret.push(ret[j].p.v); |
|
} |
|
cbFunc(cbReq, multiret, ret); |
|
} |
|
} |
|
delete _dhtgetPendingMap[locator]; |
|
} else { |
|
console.warn('_dhtgetProcessPending(): unknown locator ' + locator); |
|
} |
|
} |
|
|
|
function _dhtgetAbortPending(locator) { |
|
if (_dhtgetPendingMap[locator]) { |
|
for (var i = 0; i < _dhtgetPendingMap[locator].length; i++) { |
|
var cbFunc = _dhtgetPendingMap[locator][i].cbFunc; |
|
var cbReq = _dhtgetPendingMap[locator][i].cbReq; |
|
cbFunc(cbReq); |
|
} |
|
delete _dhtgetPendingMap[locator]; |
|
} else { |
|
console.warn('_dhtgetAbortPending(): unknown locator ' + locator); |
|
} |
|
} |
|
|
|
// get data from dht resource |
|
// the value ["v"] is extracted from response and returned to callback |
|
// null is passed to callback in case of an error |
|
function dhtget(peerAlias, resource, multi, cbFunc, cbReq, timeoutArgs) { |
|
//console.log('dhtget ' + peerAlias + ' ' + resource + ' ' + multi); |
|
var locator = _dhtgetLocator(peerAlias, resource, multi); |
|
if (_dhtgetPendingMap[locator]) { |
|
_dhtgetAddPending(locator, cbFunc, cbReq); |
|
} else { |
|
_dhtgetAddPending(locator, cbFunc, cbReq); |
|
// limit the number of simultaneous dhtgets. |
|
// this should leave some sockets for other non-blocking daemon requests. |
|
if (_dhtgetsInProgress < _maxDhtgets) { |
|
_dhtgetInternal(peerAlias, resource, multi, timeoutArgs); |
|
} else { |
|
// just queue the locator. it will be unqueue when some dhtget completes. |
|
_queuedDhtgets.push(locator); |
|
} |
|
} |
|
} |
|
|
|
// decode shortened url |
|
// the expanded url is returned to callback |
|
// null is passed to callback in case of an error |
|
function decodeShortURI(locator, cbFunc, cbReq, timeoutArgs) { |
|
if (!locator) return; |
|
if (parseInt(twisterVersion) < 93500) { |
|
console.warn('can\'t fetch URI "' + req + '" — ' |
|
+ polyglot.t('daemon_is_obsolete', {versionReq: '0.9.35'})); |
|
return; |
|
} |
|
|
|
if (_dhtgetPendingMap[locator]) { |
|
_dhtgetAddPending(locator, cbFunc, cbReq); |
|
} else { |
|
_dhtgetAddPending(locator, cbFunc, cbReq); |
|
// limit the number of simultaneous decodeshorturl's and dhtgets. |
|
// this should leave some sockets for other non-blocking daemon requests. |
|
if (_dhtgetsInProgress < _maxDhtgets) { |
|
_decodeshorturlInternal(locator, timeoutArgs); |
|
} else { |
|
// just queue the locator. it will be unqueue when some dhtget completes. |
|
_queuedDhtgets.push(locator); |
|
} |
|
} |
|
} |
|
|
|
function _dhtgetInternal(peerAlias, resource, multi, timeoutArgs) { |
|
var locator = _dhtgetLocator(peerAlias, resource, multi); |
|
_dhtgetsInProgress++; |
|
argsList = [peerAlias, resource, multi]; |
|
if (typeof timeoutArgs !== 'undefined') { |
|
argsList = argsList.concat(timeoutArgs); |
|
} |
|
twisterRpc('dhtget', argsList, |
|
function(req, ret) { |
|
_dhtgetsInProgress--; |
|
_dhtgetProcessPending(req.locator, req.multi, ret); |
|
_dhtgetDequeue(); |
|
}, {locator: locator, multi: multi}, |
|
function(req, ret) { |
|
console.warn('RPC "dhtget" error: ' + (ret && ret.message ? ret.message : ret)); |
|
_dhtgetsInProgress--; |
|
_dhtgetAbortPending(req); |
|
_dhtgetDequeue(); |
|
}, locator |
|
); |
|
} |
|
|
|
function _decodeshorturlInternal(locator, timeoutArgs) { |
|
_dhtgetsInProgress++; |
|
argsList = [locator]; |
|
if (typeof timeoutArgs !== 'undefined') { |
|
argsList = argsList.concat(timeoutArgs); |
|
} |
|
twisterRpc('decodeshorturl', argsList, |
|
function(req, ret) { |
|
_dhtgetsInProgress--; |
|
_dhtgetProcessPending(req, 'url', ret); |
|
_dhtgetDequeue(); |
|
}, locator, |
|
function(req, ret) { |
|
console.warn('RPC "decodeshorturl" error: ' + (ret && ret.message ? ret.message : ret)); |
|
_dhtgetsInProgress--; |
|
_dhtgetAbortPending(req); |
|
_dhtgetDequeue(); |
|
}, locator |
|
); |
|
} |
|
|
|
function _dhtgetDequeue() { |
|
if (_queuedDhtgets.length) { |
|
var locator = _queuedDhtgets.pop(); |
|
var locatorSplit = locator.split(';'); |
|
if (locatorSplit.length === 3) { |
|
_dhtgetInternal(locatorSplit[0], locatorSplit[1], locatorSplit[2]); |
|
} else { |
|
_decodeshorturlInternal( locator ) |
|
} |
|
} |
|
} |
|
|
|
// removes queued dhtgets (requests that have not been made to the daemon) |
|
// this is used by user search dropdown to discard old users we are not interested anymore |
|
function removeUserFromDhtgetQueue(peerAlias) { |
|
var resources = ['profile', 'avatar'] |
|
for (var i = 0; i < resources.length; i++) { |
|
var locator = _dhtgetLocator(peerAlias, resources[i], 's'); |
|
var locatorIndex = _queuedDhtgets.indexOf(locator); |
|
if (locatorIndex > -1) { |
|
_queuedDhtgets.splice(locatorIndex, 1); |
|
delete _dhtgetPendingMap[locator]; |
|
} |
|
} |
|
} |
|
|
|
function removeUsersFromDhtgetQueue(users) { |
|
for (var i = 0; i < users.length; i++) { |
|
removeUserFromDhtgetQueue(users[i]); |
|
} |
|
} |
|
|
|
// store value at the dht resource |
|
function dhtput(peerAlias, resource, multi, value, sig_user, seq, cbFunc, cbReq) { |
|
twisterRpc('dhtput', [peerAlias, resource, multi, value, sig_user, seq], |
|
function(req, ret) { |
|
if (req.cbFunc) |
|
req.cbFunc(req.cbReq, true); |
|
}, {cbFunc: cbFunc, cbReq: cbReq}, |
|
function(req, ret) { |
|
console.warn('RPC "dhtput" error: ' + (ret && ret.message ? ret.message : ret)); |
|
if (req.cbFunc) |
|
req.cbFunc(req.cbReq, false); |
|
}, {cbFunc: cbFunc, cbReq: cbReq} |
|
); |
|
} |
|
|
|
// get something from profile and store it in elem.text or do callback |
|
function getProfileResource(peerAlias, resource, elem, cbFunc, cbReq) { |
|
var profile; |
|
if (twister.profiles[peerAlias]) { |
|
profile = twister.profiles[peerAlias]; |
|
} else { |
|
profile = _getResourceFromStorage('profile:' + peerAlias); |
|
if (profile) |
|
twister.profiles[peerAlias] = profile; |
|
} |
|
if (profile) { |
|
if (elem) |
|
elem.text(profile[resource]); |
|
if (cbFunc) |
|
cbFunc(cbReq, profile[resource]); |
|
} else { |
|
loadProfile(peerAlias, |
|
function (peerAlias, req, res) { |
|
if (req.elem) |
|
req.elem.text(res[req.resource]); |
|
if (typeof req.cbFunc === 'function') |
|
req.cbFunc(req.cbReq, res[req.resource]); |
|
}, |
|
{elem: elem, resource: resource, cbFunc: cbFunc, cbReq: cbReq} |
|
); |
|
} |
|
} |
|
|
|
// get fullname and store it in elem.text |
|
function getFullname(peerAlias, elem) { |
|
elem.text(peerAlias); // fallback: set the peerAlias first in case the profile has no fullname |
|
getProfileResource(peerAlias, 'fullname', undefined, |
|
function(req, name) { |
|
if (name && (name = name.trim())) |
|
req.elem.text(name); |
|
|
|
if (typeof twisterFollowingO !== 'undefined' && // FIXME delete this check when you fix client init sequence |
|
($.Options.isFollowingMe.val === 'everywhere' || req.elem.hasClass('profile-name'))) { |
|
// here we try to detect if peer follows us and then display it |
|
if (twisterFollowingO.knownFollowers.indexOf(req.peerAlias) > -1) { |
|
req.elem.addClass('isFollowing'); |
|
req.elem.attr('title', polyglot.t('follows you')); |
|
} else if (twisterFollowingO.notFollowers.indexOf(req.peerAlias) === -1) { |
|
if (twisterFollowingO.followingsFollowings[req.peerAlias] && |
|
twisterFollowingO.followingsFollowings[req.peerAlias].following) { |
|
if (twisterFollowingO.followingsFollowings[req.peerAlias].following.indexOf(defaultScreenName) > -1) { |
|
if (twisterFollowingO.knownFollowers.indexOf(req.peerAlias) === -1) { |
|
twisterFollowingO.knownFollowers.push(req.peerAlias); |
|
twisterFollowingO.save(); |
|
addPeerToFollowersList(getElem('.followers-modal .followers-list'), req.peerAlias, true); |
|
$('.module.mini-profile .open-followers') |
|
.attr('title', twisterFollowingO.knownFollowers.length.toString()); |
|
} |
|
req.elem.addClass('isFollowing'); |
|
req.elem.attr('title', polyglot.t('follows you')); |
|
} |
|
} else { |
|
loadFollowingFromDht(req.peerAlias, 1, [], 0, |
|
function (req, following, seqNum) { |
|
if (following.indexOf(defaultScreenName) > -1) { |
|
if (twisterFollowingO.knownFollowers.indexOf(req.peerAlias) === -1) { |
|
twisterFollowingO.knownFollowers.push(req.peerAlias); |
|
addPeerToFollowersList(getElem('.followers-modal .followers-list'), req.peerAlias, true); |
|
$('.module.mini-profile .open-followers') |
|
.attr('title', twisterFollowingO.knownFollowers.length.toString()); |
|
} |
|
req.elem.addClass('isFollowing'); |
|
req.elem.attr('title', polyglot.t('follows you')); |
|
} else if (twisterFollowingO.notFollowers.indexOf(req.peerAlias) === -1) |
|
twisterFollowingO.notFollowers.push(req.peerAlias); |
|
|
|
twisterFollowingO.save(); |
|
}, {elem: req.elem, peerAlias: req.peerAlias} |
|
); |
|
} |
|
} |
|
} |
|
}, {elem: elem, peerAlias: peerAlias} |
|
); |
|
} |
|
|
|
// get bio, format it as post message and store result to elem |
|
function getBioToElem(peerAlias, elem) { |
|
getProfileResource(peerAlias, 'bio', undefined, fillElemWithTxt, elem); |
|
} |
|
|
|
// get tox address and store it in elem.text |
|
function getTox(peerAlias, elem) { |
|
getProfileResource(peerAlias, 'tox', false, |
|
function(elem, val) { |
|
if (val) { |
|
elem.attr('href', 'tox:' + val); |
|
elem.next().attr('data', val).attr('title', 'Copy to clipboard'); |
|
elem.parent().css('display', 'inline-block').parent().show(); |
|
} |
|
}, elem |
|
); |
|
} |
|
|
|
// get bitmessage address and store it in elem.text |
|
function getBitmessage(peerAlias, elem) { |
|
getProfileResource(peerAlias, 'bitmessage', false, |
|
function(elem, val) { |
|
if (val) { |
|
elem.attr('href', 'bitmsg:' + val + '?action=add&label=' + peerAlias); |
|
elem.next().attr('data', val).attr('title', 'Copy to clipboard'); |
|
elem.parent().css('display', 'inline-block').parent().show(); |
|
} |
|
}, elem |
|
); |
|
} |
|
|
|
// get location and store it in elem.text |
|
function getLocation(peerAlias, elem) { |
|
getProfileResource(peerAlias, 'location', elem); |
|
} |
|
|
|
// get location and store it in elem.text |
|
function getWebpage(peerAlias, elem) { |
|
getProfileResource(peerAlias, 'url', elem, |
|
function(elem, val) { |
|
if (typeof(val) !== 'undefined') { |
|
if (val.indexOf('://') < 0) { |
|
val = 'http://' + val; |
|
} |
|
elem.attr('href', val); |
|
} |
|
}, elem |
|
); |
|
} |
|
|
|
function getGroupChatName(groupAlias, elem) { |
|
twisterRpc('getgroupinfo', [groupAlias], |
|
function(elem, ret) { |
|
elem.text(ret.description); |
|
}, elem, |
|
function(req, ret) { |
|
console.warn('RPC "getgroupinfo" error: ' + (ret && ret.message ? ret.message : ret)); |
|
req.elem.text(req.groupAlias); |
|
}, {elem: elem, groupAlias: groupAlias} |
|
); |
|
} |
|
|
|
// we must cache avatar results to disk to lower bandwidth on |
|
// other peers. dht server limits udp rate so requesting too much |
|
// data will only cause new requests to fail. |
|
function _getResourceFromStorage(locator) { |
|
var storage = $.localStorage; |
|
if (storage.isSet(locator)) { |
|
var storedResource = storage.get(locator); |
|
var curTime = new Date().getTime() / 1000; |
|
// avatar is downloaded once per day FIXME why once per day? what about profiles? |
|
// FIXME need to check what type of data is requested and what time is allowed for it |
|
if (storedResource.time + 86400 > curTime) { // 3600 * 24 |
|
return storedResource.data; |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
function _putResourceIntoStorage(locator, data) { |
|
$.localStorage.set(locator, { |
|
time: Math.trunc(new Date().getTime() / 1000), |
|
data: data |
|
}); |
|
} |
|
|
|
function cleanupStorage() { |
|
var curTime = new Date().getTime() / 1000; |
|
var storage = $.localStorage, keys = storage.keys(), item = ''; |
|
var delAvatars = delProfiles = 0; |
|
|
|
for (var i = 0; i < keys.length; i++) { |
|
item = keys[i]; |
|
// FIXME need to decide what time for type of data is allowed |
|
if (item.substr(0, 7) === 'avatar:') { |
|
if (storage.get(item).time + 86400 < curTime) { // 3600 * 24 hours |
|
storage.remove(item); |
|
delAvatars++; |
|
//console.log('local storage item \'' + item + '\' was too old, deleted'); |
|
} |
|
} else if (item.substr(0, 8) === 'profile:') { |
|
if (storage.get(item).time + 86400 < curTime) { // 3600 * 24 hours |
|
storage.remove(item); |
|
delProfiles++; |
|
//console.log('local storage item \'' + item + '\' was too old, deleted'); |
|
} |
|
} |
|
} |
|
|
|
console.log('cleaning of storage is completed for ' + (new Date().getTime() / 1000 - curTime) + 's'); |
|
if (delAvatars) console.log(' ' + delAvatars + ' cached avatars was too old, deleted'); |
|
if (delProfiles) console.log(' ' + delProfiles + ' cached profiles was too old, deleted'); |
|
console.log(' ' + 'there was ' + i + ' items in total, now ' + (i - delAvatars - delProfiles)); |
|
} |
|
|
|
// get avatar and set it in img.attr("src") |
|
// TODO rename to getAvatarImgToELem(), move nin theme related stuff to nin's theme_option.js |
|
function getAvatar(peerAlias, img) { |
|
if (!img.length) |
|
return; |
|
|
|
if (peerAlias === 'nobody') { |
|
|
|
var avatar = 'img/tornado_avatar.png'; |
|
|
|
switch ($.Options.theme.val) { |
|
case 'nin': |
|
avatar = 'theme_nin/img/tornado_avatar.png'; |
|
break; |
|
case 'nin_night': |
|
avatar = 'theme_nin_night/img/tornado_avatar.png'; |
|
break; |
|
case 'nin_original': |
|
avatar = 'theme_nin_original/img/tornado_avatar.png'; |
|
break; |
|
} |
|
|
|
img.attr('src', avatar); |
|
|
|
return; |
|
} |
|
|
|
if (twister.avatars[peerAlias]) { |
|
img.attr('src', twister.avatars[peerAlias].src); |
|
} else { |
|
var data = _getResourceFromStorage('avatar:' + peerAlias); |
|
|
|
if (data) { |
|
if (typeof data !== 'object') |
|
data = {src: data, version: 0}; |
|
|
|
switch (data.src.substr(0, 4)) { |
|
case 'jpg/': |
|
data.src = 'data:image/jpeg;base64,/9j/' + window.btoa(data.src.slice(4)); |
|
break; |
|
case 'png/': |
|
data.src = 'data:image/png;base64,' + window.btoa(data.src.slice(4)); |
|
break; |
|
case 'gif/': |
|
data.src = 'data:image/gif;base64,' + window.btoa(data.src.slice(4)); |
|
break; |
|
} |
|
twister.avatars[peerAlias] = data; |
|
img.attr('src', data.src); |
|
} else { |
|
loadAvatar(peerAlias, |
|
function (peerAlias, req, res) { |
|
req.attr('src', res); |
|
}, |
|
img |
|
); |
|
} |
|
} |
|
} |
|
|
|
function loadProfile(peerAlias, cbFunc, cbReq) { |
|
dhtget(peerAlias, 'profile', 's', |
|
function(req, res, resRaw) { |
|
if (!resRaw || typeof res !== 'object') |
|
return; |
|
|
|
res.version = parseInt(resRaw[0].p.seq); |
|
|
|
if (!twister.profiles[req.peerAlias] || !twister.profiles[req.peerAlias].version |
|
|| res.version > twister.profiles[req.peerAlias].version) { |
|
console.log('got ' + req.peerAlias + '\'s profile version ' + res.version + ' — going to cache and redraw globally'); |
|
cacheProfile(req.peerAlias, res); |
|
redrawProfile(req.peerAlias, res); |
|
} |
|
|
|
if (typeof req.cbFunc === 'function') |
|
req.cbFunc(req.peerAlias, req.cbReq, res); |
|
}, |
|
{peerAlias: peerAlias, cbFunc: cbFunc, cbReq: cbReq} |
|
); |
|
} |
|
|
|
function loadAvatar(peerAlias, cbFunc, cbReq) { |
|
dhtget(peerAlias, 'avatar', 's', |
|
function(req, res, resRaw) { |
|
if (!resRaw) |
|
return; |
|
|
|
if (!res) |
|
res = 'img/genericPerson.png'; |
|
|
|
var version = parseInt(resRaw[0].p.seq); |
|
|
|
if (!twister.avatars[req.peerAlias] || !twister.avatars[req.peerAlias].version |
|
|| version > twister.avatars[req.peerAlias].version) { |
|
console.log('got ' + req.peerAlias + '\'s avatar version ' + version + ' — going to cache and redraw globally'); |
|
cacheAvatar(req.peerAlias, res, version); |
|
redrawAvatar(req.peerAlias, res); |
|
} |
|
|
|
if (typeof req.cbFunc === 'function') |
|
req.cbFunc(req.peerAlias, req.cbReq, res); |
|
}, |
|
{peerAlias: peerAlias, cbFunc: cbFunc, cbReq: cbReq} |
|
); |
|
} |
|
|
|
function cacheProfile(peerAlias, req) { |
|
var dat = {}; |
|
for (var i in req) |
|
if (req[i]) |
|
dat[i] = req[i]; |
|
|
|
twister.profiles[peerAlias] = dat; |
|
|
|
_putResourceIntoStorage('profile:' + peerAlias, dat); |
|
} |
|
|
|
function cacheAvatar(peerAlias, req, version) { |
|
twister.avatars[peerAlias] = {src: req, version: version}; |
|
|
|
if (req === 'img/genericPerson.png') |
|
return; |
|
|
|
if (req.substr(0, 27) === 'data:image/jpeg;base64,/9j/') { |
|
req = 'jpg/' + window.atob(req.slice(27)); |
|
} else { |
|
var s = req.substr(0, 22); |
|
if (s === 'data:image/png;base64,' || s === 'data:image/gif;base64,') |
|
req = req.substr(11, 3) + '/' + window.atob(req.slice(22)); |
|
} |
|
_putResourceIntoStorage('avatar:' + peerAlias, {src: req, version: version}); |
|
} |
|
|
|
function saveProfile(peerAlias, req, cbFunc, cbReq, cbErrFunc, cbErrReq) { |
|
if (twister.profiles[peerAlias] && twister.profiles[peerAlias].version) |
|
req.version = ++twister.profiles[peerAlias].version; |
|
else |
|
req.version = 1; |
|
|
|
cacheProfile(peerAlias, req); |
|
|
|
var dat = {}; |
|
for (var i in req) |
|
if (i !== 'version' && req[i]) |
|
dat[i] = req[i]; |
|
|
|
dhtput(peerAlias, 'profile', 's', dat, |
|
peerAlias, req.version, |
|
function (req, res) { |
|
if (!res) { |
|
if (typeof req.cbErrFunc === 'function') |
|
req.cbErrFunc(req.cbErrReq); |
|
|
|
return; |
|
} |
|
|
|
if (typeof req.cbFunc === 'function') |
|
req.cbFunc(req.cbReq); |
|
}, |
|
{cbFunc: cbFunc, cbReq: cbReq, cbErrFunc: cbErrFunc, cbErrReq: cbErrReq} |
|
); |
|
} |
|
|
|
function saveAvatar(peerAlias, req, cbFunc, cbReq, cbErrFunc, cbErrReq) { |
|
if (twister.avatars[peerAlias] && twister.avatars[peerAlias].version) |
|
var version = ++twister.avatars[peerAlias].version; |
|
else |
|
var version = 1; |
|
|
|
cacheAvatar(peerAlias, req, version); |
|
|
|
dhtput(peerAlias, 'avatar', 's', req, |
|
peerAlias, version, |
|
function (req, res) { |
|
if (!res) { |
|
if (typeof req.cbErrFunc === 'function') |
|
req.cbErrFunc(req.cbErrReq); |
|
|
|
return; |
|
} |
|
|
|
if (typeof req.cbFunc === 'function') |
|
req.cbFunc(req.cbReq); |
|
}, |
|
{cbFunc: cbFunc, cbReq: cbReq, cbErrFunc: cbErrFunc, cbErrReq: cbErrReq} |
|
); |
|
} |
|
|
|
// get estimative for number of followers (use known peers of torrent tracker) |
|
function getFollowers(peerAlias, elem) { |
|
dhtget(peerAlias, 'tracker', 'm', |
|
function(elem, ret) { |
|
if (ret && ret.length && ret[0].followers) { |
|
elem.text(ret[0].followers) |
|
} |
|
}, elem |
|
); |
|
} |
|
|
|
function getPostsCount(peerAlias, elem) { |
|
dhtget(peerAlias, 'status', 's', |
|
function(req, v) { |
|
var count = 0; |
|
if (v && v.userpost) { |
|
count = v.userpost.k + 1; |
|
} |
|
var oldCount = parseInt(req.elem.text()); |
|
if (!oldCount || count > oldCount) { |
|
req.elem.text(count); |
|
} |
|
if (peerAlias === defaultScreenName && count) { |
|
incLastPostId(v.userpost.k); |
|
} |
|
}, {peerAlias: peerAlias, elem: elem} |
|
); |
|
} |
|
|
|
function getStatusTime(peerAlias, elem) { |
|
dhtget(peerAlias, 'status', 's', |
|
function (req, ret) { |
|
if (!ret || !ret.userpost) |
|
return; |
|
|
|
req.elem.text(timeGmtToText(ret.userpost.time)) |
|
.closest('.latest-activity') |
|
.attr('data-screen-name', req.peerAlias) |
|
.attr('data-id', ret.userpost.k) |
|
.attr('data-time', ret.userpost.time) |
|
; |
|
}, {peerAlias: peerAlias, elem: elem} |
|
); |
|
} |
|
|
|
function getPostMaxAvailability(peerAlias, k, cbFunc, cbReq) { |
|
twisterRpc('getpiecemaxseen', [peerAlias, k], |
|
function(req, ret) { |
|
req.cbFunc(req.cbReq, ret); |
|
}, {cbFunc: cbFunc, cbReq: cbReq}, |
|
function(req, ret) { |
|
console.warn('RPC "getpiecemaxseen" error: ' + (ret && ret.message ? ret.message : ret)); |
|
} |
|
); |
|
} |
|
|
|
function checkPubkeyExists(peerAlias, cbFunc, cbReq) { |
|
// pubkey is checked in block chain db. |
|
// so only accepted registrations are reported (local wallet users are not) |
|
twisterRpc('dumppubkey', [peerAlias], |
|
function(req, ret) { |
|
req.cbFunc(req.cbReq, ret.length > 0); |
|
}, {cbFunc: cbFunc, cbReq: cbReq}, |
|
function(req, ret) { |
|
console.warn('RPC "dumppubkey" error: ' + (ret && ret.message ? ret.message : ret)); |
|
alert(polyglot.t('error_connecting_to_daemon')); |
|
} |
|
); |
|
} |
|
|
|
// pubkey is obtained from block chain db. |
|
// so only accepted registrations are reported (local wallet users are not) |
|
// cbFunc is called as cbFunc(cbReq, pubkey) |
|
// if user doesn't exist then pubkey.length == 0 |
|
function dumpPubkey(peerAlias, cbFunc, cbReq) { |
|
if (_pubkeyMap[peerAlias]) { |
|
if (cbFunc) |
|
cbFunc(cbReq, _pubkeyMap[peerAlias]); |
|
} else { |
|
twisterRpc('dumppubkey', [peerAlias], |
|
function (req, ret) { |
|
if (ret.length > 0) { |
|
_pubkeyMap[peerAlias] = ret; |
|
} |
|
if (req.cbFunc) { |
|
req.cbFunc(req.cbReq, ret); |
|
} |
|
}, {cbFunc: cbFunc, cbReq: cbReq}, |
|
function (req, ret) { |
|
console.warn('RPC "dumppubkey" error: ' + (ret && ret.message ? ret.message : ret)); |
|
alert(polyglot.t('error_connecting_to_daemon')); |
|
} |
|
); |
|
} |
|
} |
|
|
|
// privkey is obtained from wallet db |
|
// so privkey is returned even for unsent transactions |
|
function dumpPrivkey(peerAlias, cbFunc, cbReq) { |
|
twisterRpc('dumpprivkey', [peerAlias], |
|
function(req, ret) { |
|
req.cbFunc(req.cbReq, ret); |
|
}, {cbFunc: cbFunc, cbReq: cbReq}, |
|
function(req, ret) { |
|
req.cbFunc(req.cbReq, ''); |
|
console.warn('user unknown — RPC "dumppubkey" error: ' + (ret && ret.message ? ret.message : ret)); |
|
}, {cbFunc: cbFunc, cbReq: cbReq} |
|
); |
|
}
|
|
|