@ -25,9 +25,9 @@ var sumChannelSizes = function (sizes) {
// FIXME it's possible for this to respond before the server has had a chance
// FIXME it's possible for this to respond before the server has had a chance
// to fetch the limits. Maybe we should respond with an error...
// to fetch the limits. Maybe we should respond with an error...
// or wait until we actually know the limits before responding
// or wait until we actually know the limits before responding
var getLimit = Pinning . getLimit = function ( Env , public Key, cb ) {
var getLimit = Pinning . getLimit = function ( Env , safe Key, cb ) {
var un escapedKey = unescapeKeyCharacters ( public Key) ;
var un safeKey = unescapeKeyCharacters ( safe Key) ;
var limit = Env . limits [ un escaped Key] ;
var limit = Env . limits [ un safe Key] ;
var defaultLimit = typeof ( Env . defaultStorageLimit ) === 'number' ?
var defaultLimit = typeof ( Env . defaultStorageLimit ) === 'number' ?
Env . defaultStorageLimit : Core . DEFAULT _LIMIT ;
Env . defaultStorageLimit : Core . DEFAULT _LIMIT ;
@ -37,32 +37,89 @@ var getLimit = Pinning.getLimit = function (Env, publicKey, cb) {
cb ( void 0 , toSend ) ;
cb ( void 0 , toSend ) ;
} ;
} ;
const answerDeferred = function ( Env , channel , bool ) {
const pending = Env . pendingPinInquiries ;
const stack = pending [ channel ] ;
if ( ! Array . isArray ( stack ) ) { return ; }
delete pending [ channel ] ;
stack . forEach ( function ( cb ) {
cb ( void 0 , bool ) ;
} ) ;
} ;
var addPinned = function (
var addPinned = function (
Env ,
Env ,
publicKey /*:string*/ ,
safe Key /*:string*/ ,
channelList /*Array<string>*/ ,
channelList /*Array<string>*/ ,
cb /*:()=>void*/ )
cb /*:()=>void*/ )
{
{
Env . evPinnedPadsReady . reg ( ( ) => {
channelList . forEach ( function ( channel ) {
channelList . forEach ( ( c ) => {
Pins . addUserPinToState ( Env . pinnedPads , safeKey , channel ) ;
const x = Env . pinnedPads [ c ] = Env . pinnedPads [ c ] || { } ;
answerDeferred ( Env , channel , true ) ;
x [ publicKey ] = 1 ;
} ) ;
} ) ;
cb ( ) ;
} ;
const isEmpty = function ( obj ) {
if ( ! obj || typeof ( obj ) !== 'object' ) { return true ; }
for ( var key in obj ) {
if ( obj . hasOwnProperty ( key ) ) { return true ; }
}
return false ;
} ;
const deferUserTask = function ( Env , safeKey , deferred ) {
const pending = Env . pendingUnpins ;
( pending [ safeKey ] = pending [ safeKey ] || [ ] ) . push ( deferred ) ;
} ;
const runUserDeferred = function ( Env , safeKey ) {
const pending = Env . pendingUnpins ;
const stack = pending [ safeKey ] ;
if ( ! Array . isArray ( stack ) ) { return ; }
delete pending [ safeKey ] ;
stack . forEach ( function ( cb ) {
cb ( ) ;
cb ( ) ;
} ) ;
} ) ;
} ;
} ;
const runRemainingDeferred = function ( Env ) {
const pending = Env . pendingUnpins ;
for ( var safeKey in pending ) {
runUserDeferred ( Env , safeKey ) ;
}
} ;
const removeSelfFromPinned = function ( Env , safeKey , channelList ) {
channelList . forEach ( function ( channel ) {
const channelPinStatus = Env . pinnedPads [ channel ] ;
if ( ! channelPinStatus ) { return ; }
delete channelPinStatus [ safeKey ] ;
if ( isEmpty ( channelPinStatus ) ) {
delete Env . pinnedPads [ channel ] ;
}
} ) ;
} ;
var removePinned = function (
var removePinned = function (
Env ,
Env ,
publicKey /*:string*/ ,
safe Key /*:string*/ ,
channelList /*Array<string>*/ ,
channelList /*Array<string>*/ ,
cb /*:()=>void*/ )
cb /*:()=>void*/ )
{
{
Env . evPinnedPadsReady . reg ( ( ) => {
channelList . forEach ( ( c ) => {
// if pins are already loaded then you can just unpin normally
const x = Env . pinnedPads [ c ] ;
if ( Env . pinsLoaded ) {
if ( ! x ) { return ; }
removeSelfFromPinned ( Env , safeKey , channelList ) ;
delete x [ publicKey ] ;
return void cb ( ) ;
} ) ;
}
// otherwise defer until later...
deferUserTask ( Env , safeKey , function ( ) {
removeSelfFromPinned ( Env , safeKey , channelList ) ;
cb ( ) ;
cb ( ) ;
} ) ;
} ) ;
} ;
} ;
@ -100,24 +157,24 @@ var getMultipleFileSize = function (Env, channels, cb) {
} ;
} ;
const batchUserPins = BatchRead ( "LOAD_USER_PINS" ) ;
const batchUserPins = BatchRead ( "LOAD_USER_PINS" ) ;
var loadUserPins = function ( Env , public Key, cb ) {
var loadUserPins = function ( Env , safe Key, cb ) {
var session = Core . getSession ( Env . Sessions , public Key) ;
var session = Core . getSession ( Env . Sessions , safe Key) ;
if ( session . channels ) {
if ( session . channels ) {
return cb ( session . channels ) ;
return cb ( session . channels ) ;
}
}
batchUserPins ( public Key, cb , function ( done ) {
batchUserPins ( safe Key, cb , function ( done ) {
var ref = { } ;
var ref = { } ;
var lineHandler = Pins . createLineHandler ( ref , function ( label , data ) {
var lineHandler = Pins . createLineHandler ( ref , function ( label , data ) {
Env . Log . error ( label , {
Env . Log . error ( label , {
log : public Key,
log : safe Key,
data : data ,
data : data ,
} ) ;
} ) ;
} ) ;
} ) ;
// if channels aren't in memory. load them from disk
// if channels aren't in memory. load them from disk
Env . pinStore . getMessages ( public Key, lineHandler , function ( ) {
Env . pinStore . getMessages ( safe Key, lineHandler , function ( ) {
// no more messages
// no more messages
// only put this into the cache if it completes
// only put this into the cache if it completes
@ -133,27 +190,27 @@ var truthyKeys = function (O) {
} ) ;
} ) ;
} ;
} ;
var getChannelList = Pinning . getChannelList = function ( Env , public Key, _cb ) {
var getChannelList = Pinning . getChannelList = function ( Env , safe Key, _cb ) {
var cb = Util . once ( Util . mkAsync ( _cb ) ) ;
var cb = Util . once ( Util . mkAsync ( _cb ) ) ;
loadUserPins ( Env , public Key, function ( pins ) {
loadUserPins ( Env , safe Key, function ( pins ) {
cb ( truthyKeys ( pins ) ) ;
cb ( truthyKeys ( pins ) ) ;
} ) ;
} ) ;
} ;
} ;
const batchTotalSize = BatchRead ( "GET_TOTAL_SIZE" ) ;
const batchTotalSize = BatchRead ( "GET_TOTAL_SIZE" ) ;
Pinning . getTotalSize = function ( Env , public Key, cb ) {
Pinning . getTotalSize = function ( Env , safe Key, cb ) {
var un escapedKey = unescapeKeyCharacters ( public Key) ;
var un safeKey = unescapeKeyCharacters ( safe Key) ;
var limit = Env . limits [ un escaped Key] ;
var limit = Env . limits [ un safe Key] ;
// Get a common key if multiple users share the same quota, otherwise take the public key
// Get a common key if multiple users share the same quota, otherwise take the public key
var batchKey = ( limit && Array . isArray ( limit . users ) ) ? limit . users . join ( '' ) : public Key;
var batchKey = ( limit && Array . isArray ( limit . users ) ) ? limit . users . join ( '' ) : safe Key;
batchTotalSize ( batchKey , cb , function ( done ) {
batchTotalSize ( batchKey , cb , function ( done ) {
var channels = [ ] ;
var channels = [ ] ;
var bytes = 0 ;
var bytes = 0 ;
nThen ( function ( waitFor ) {
nThen ( function ( waitFor ) {
// Get the channels list for our user account
// Get the channels list for our user account
Pinning. getChannelList( Env , public Key, waitFor ( function ( _channels ) {
getChannelList( Env , safe Key, waitFor ( function ( _channels ) {
if ( ! _channels ) {
if ( ! _channels ) {
waitFor . abort ( ) ;
waitFor . abort ( ) ;
return done ( 'INVALID_PIN_LIST' ) ;
return done ( 'INVALID_PIN_LIST' ) ;
@ -163,7 +220,7 @@ Pinning.getTotalSize = function (Env, publicKey, cb) {
// Get the channels list for users sharing our quota
// Get the channels list for users sharing our quota
if ( limit && Array . isArray ( limit . users ) && limit . users . length > 1 ) {
if ( limit && Array . isArray ( limit . users ) && limit . users . length > 1 ) {
limit . users . forEach ( function ( key ) {
limit . users . forEach ( function ( key ) {
if ( key === un escaped Key) { return ; } // Don't count ourselves twice
if ( key === un safe Key) { return ; } // Don't count ourselves twice
getChannelList ( Env , key , waitFor ( function ( _channels ) {
getChannelList ( Env , key , waitFor ( function ( _channels ) {
if ( ! _channels ) { return ; } // Broken user, don't count their quota
if ( ! _channels ) { return ; } // Broken user, don't count their quota
Array . prototype . push . apply ( channels , _channels ) ;
Array . prototype . push . apply ( channels , _channels ) ;
@ -207,10 +264,10 @@ Pinning.trimPins = function (Env, safeKey, cb) {
cb ( "NOT_IMPLEMENTED" ) ;
cb ( "NOT_IMPLEMENTED" ) ;
} ;
} ;
var getFreeSpace = Pinning . getFreeSpace = function ( Env , public Key, cb ) {
var getFreeSpace = Pinning . getFreeSpace = function ( Env , safe Key, cb ) {
getLimit ( Env , public Key, function ( e , limit ) {
getLimit ( Env , safe Key, function ( e , limit ) {
if ( e ) { return void cb ( e ) ; }
if ( e ) { return void cb ( e ) ; }
Pinning . getTotalSize ( Env , public Key, function ( e , size ) {
Pinning . getTotalSize ( Env , safe Key, function ( e , size ) {
if ( typeof ( size ) === 'undefined' ) { return void cb ( e ) ; }
if ( typeof ( size ) === 'undefined' ) { return void cb ( e ) ; }
var rem = limit [ 0 ] - size ;
var rem = limit [ 0 ] - size ;
@ -236,20 +293,20 @@ var hashChannelList = function (A) {
return hash ;
return hash ;
} ;
} ;
var getHash = Pinning . getHash = function ( Env , public Key, cb ) {
var getHash = Pinning . getHash = function ( Env , safe Key, cb ) {
getChannelList ( Env , public Key, function ( channels ) {
getChannelList ( Env , safe Key, function ( channels ) {
cb ( void 0 , hashChannelList ( channels ) ) ;
cb ( void 0 , hashChannelList ( channels ) ) ;
} ) ;
} ) ;
} ;
} ;
Pinning . pinChannel = function ( Env , public Key, channels , cb ) {
Pinning . pinChannel = function ( Env , safe Key, channels , cb ) {
if ( ! channels && channels . filter ) {
if ( ! channels && channels . filter ) {
return void cb ( 'INVALID_PIN_LIST' ) ;
return void cb ( 'INVALID_PIN_LIST' ) ;
}
}
// get channel list ensures your session has a cached channel list
// get channel list ensures your session has a cached channel list
getChannelList ( Env , public Key, function ( pinned ) {
getChannelList ( Env , safe Key, function ( pinned ) {
var session = Core . getSession ( Env . Sessions , public Key) ;
var session = Core . getSession ( Env . Sessions , safe Key) ;
// only pin channels which are not already pinned
// only pin channels which are not already pinned
var toStore = channels . filter ( function ( channel ) {
var toStore = channels . filter ( function ( channel ) {
@ -257,42 +314,42 @@ Pinning.pinChannel = function (Env, publicKey, channels, cb) {
} ) ;
} ) ;
if ( toStore . length === 0 ) {
if ( toStore . length === 0 ) {
return void getHash ( Env , public Key, cb ) ;
return void getHash ( Env , safe Key, cb ) ;
}
}
getMultipleFileSize ( Env , toStore , function ( e , sizes ) {
getMultipleFileSize ( Env , toStore , function ( e , sizes ) {
if ( typeof ( sizes ) === 'undefined' ) { return void cb ( e ) ; }
if ( typeof ( sizes ) === 'undefined' ) { return void cb ( e ) ; }
var pinSize = sumChannelSizes ( sizes ) ;
var pinSize = sumChannelSizes ( sizes ) ;
getFreeSpace ( Env , public Key, function ( e , free ) {
getFreeSpace ( Env , safe Key, function ( e , free ) {
if ( typeof ( free ) === 'undefined' ) {
if ( typeof ( free ) === 'undefined' ) {
Env . WARN ( 'getFreeSpace' , e ) ;
Env . WARN ( 'getFreeSpace' , e ) ;
return void cb ( e ) ;
return void cb ( e ) ;
}
}
if ( pinSize > free ) { return void cb ( 'E_OVER_LIMIT' ) ; }
if ( pinSize > free ) { return void cb ( 'E_OVER_LIMIT' ) ; }
Env . pinStore . message ( public Key, JSON . stringify ( [ 'PIN' , toStore , + new Date ( ) ] ) ,
Env . pinStore . message ( safe Key, JSON . stringify ( [ 'PIN' , toStore , + new Date ( ) ] ) ,
function ( e ) {
function ( e ) {
if ( e ) { return void cb ( e ) ; }
if ( e ) { return void cb ( e ) ; }
toStore . forEach ( function ( channel ) {
toStore . forEach ( function ( channel ) {
session . channels [ channel ] = true ;
session . channels [ channel ] = true ;
} ) ;
} ) ;
addPinned ( Env , public Key, toStore , ( ) => { } ) ;
addPinned ( Env , safe Key, toStore , ( ) => { } ) ;
getHash ( Env , public Key, cb ) ;
getHash ( Env , safe Key, cb ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ;
} ;
Pinning . unpinChannel = function ( Env , public Key, channels , cb ) {
Pinning . unpinChannel = function ( Env , safe Key, channels , cb ) {
if ( ! channels && channels . filter ) {
if ( ! channels && channels . filter ) {
// expected array
// expected array
return void cb ( 'INVALID_PIN_LIST' ) ;
return void cb ( 'INVALID_PIN_LIST' ) ;
}
}
getChannelList ( Env , public Key, function ( pinned ) {
getChannelList ( Env , safe Key, function ( pinned ) {
var session = Core . getSession ( Env . Sessions , public Key) ;
var session = Core . getSession ( Env . Sessions , safe Key) ;
// only unpin channels which are pinned
// only unpin channels which are pinned
var toStore = channels . filter ( function ( channel ) {
var toStore = channels . filter ( function ( channel ) {
@ -300,27 +357,27 @@ Pinning.unpinChannel = function (Env, publicKey, channels, cb) {
} ) ;
} ) ;
if ( toStore . length === 0 ) {
if ( toStore . length === 0 ) {
return void getHash ( Env , public Key, cb ) ;
return void getHash ( Env , safe Key, cb ) ;
}
}
Env . pinStore . message ( public Key, JSON . stringify ( [ 'UNPIN' , toStore , + new Date ( ) ] ) ,
Env . pinStore . message ( safe Key, JSON . stringify ( [ 'UNPIN' , toStore , + new Date ( ) ] ) ,
function ( e ) {
function ( e ) {
if ( e ) { return void cb ( e ) ; }
if ( e ) { return void cb ( e ) ; }
toStore . forEach ( function ( channel ) {
toStore . forEach ( function ( channel ) {
delete session . channels [ channel ] ;
delete session . channels [ channel ] ;
} ) ;
} ) ;
removePinned ( Env , public Key, toStore , ( ) => { } ) ;
removePinned ( Env , safe Key, toStore , ( ) => { } ) ;
getHash ( Env , public Key, cb ) ;
getHash ( Env , safe Key, cb ) ;
} ) ;
} ) ;
} ) ;
} ) ;
} ;
} ;
Pinning . resetUserPins = function ( Env , public Key, channelList , cb ) {
Pinning . resetUserPins = function ( Env , safe Key, channelList , cb ) {
if ( ! Array . isArray ( channelList ) ) { return void cb ( 'INVALID_PIN_LIST' ) ; }
if ( ! Array . isArray ( channelList ) ) { return void cb ( 'INVALID_PIN_LIST' ) ; }
var session = Core . getSession ( Env . Sessions , public Key) ;
var session = Core . getSession ( Env . Sessions , safe Key) ;
if ( ! channelList . length ) {
if ( ! channelList . length ) {
return void getHash ( Env , public Key, function ( e , hash ) {
return void getHash ( Env , safe Key, function ( e , hash ) {
if ( e ) { return cb ( e ) ; }
if ( e ) { return cb ( e ) ; }
cb ( void 0 , hash ) ;
cb ( void 0 , hash ) ;
} ) ;
} ) ;
@ -332,7 +389,7 @@ Pinning.resetUserPins = function (Env, publicKey, channelList, cb) {
var pinSize = sumChannelSizes ( sizes ) ;
var pinSize = sumChannelSizes ( sizes ) ;
getLimit ( Env , public Key, function ( e , limit ) {
getLimit ( Env , safe Key, function ( e , limit ) {
if ( e ) {
if ( e ) {
Env . WARN ( '[RESET_ERR]' , e ) ;
Env . WARN ( '[RESET_ERR]' , e ) ;
return void cb ( e ) ;
return void cb ( e ) ;
@ -347,7 +404,7 @@ Pinning.resetUserPins = function (Env, publicKey, channelList, cb) {
They will not be able to pin additional pads until they upgrade
They will not be able to pin additional pads until they upgrade
or delete enough files to go back under their limit . * /
or delete enough files to go back under their limit . * /
if ( pinSize > limit [ 0 ] && session . hasPinned ) { return void ( cb ( 'E_OVER_LIMIT' ) ) ; }
if ( pinSize > limit [ 0 ] && session . hasPinned ) { return void ( cb ( 'E_OVER_LIMIT' ) ) ; }
Env . pinStore . message ( public Key, JSON . stringify ( [ 'RESET' , channelList , + new Date ( ) ] ) ,
Env . pinStore . message ( safe Key, JSON . stringify ( [ 'RESET' , channelList , + new Date ( ) ] ) ,
function ( e ) {
function ( e ) {
if ( e ) { return void cb ( e ) ; }
if ( e ) { return void cb ( e ) ; }
channelList . forEach ( function ( channel ) {
channelList . forEach ( function ( channel ) {
@ -360,13 +417,13 @@ Pinning.resetUserPins = function (Env, publicKey, channelList, cb) {
} else {
} else {
oldChannels = [ ] ;
oldChannels = [ ] ;
}
}
removePinned ( Env , public Key, oldChannels , ( ) => {
removePinned ( Env , safe Key, oldChannels , ( ) => {
addPinned ( Env , public Key, channelList , ( ) => { } ) ;
addPinned ( Env , safe Key, channelList , ( ) => { } ) ;
} ) ;
} ) ;
// update in-memory cache IFF the reset was allowed.
// update in-memory cache IFF the reset was allowed.
session . channels = pins ;
session . channels = pins ;
getHash ( Env , public Key, function ( e , hash ) {
getHash ( Env , safe Key, function ( e , hash ) {
cb ( e , hash ) ;
cb ( e , hash ) ;
} ) ;
} ) ;
} ) ;
} ) ;
@ -429,35 +486,74 @@ Pinning.getDeletedPads = function (Env, channels, cb) {
} ) ;
} ) ;
} ;
} ;
const answerNoConclusively = function ( Env ) {
const pending = Env . pendingPinInquiries ;
for ( var channel in pending ) {
answerDeferred ( Env , channel , false ) ;
}
} ;
// inform that the
// inform that the
Pinning . loadChannelPins = function ( Env ) {
Pinning . loadChannelPins = function ( Env ) {
Pins . list ( function ( err , data ) {
const stats = {
surplus : 0 ,
pinned : 0 ,
duplicated : 0 ,
users : 0 , // XXX useful for admin panel ?
} ;
const handler = function ( ref , safeKey , pinned ) {
if ( ref . surplus ) {
stats . surplus += ref . surplus ;
}
for ( var channel in ref . pins ) {
if ( ! pinned . hasOwnProperty ( channel ) ) {
answerDeferred ( Env , channel , true ) ;
stats . pinned ++ ;
} else {
stats . duplicated ++ ;
}
}
stats . users ++ ;
runUserDeferred ( Env , safeKey ) ;
} ;
Pins . list ( function ( err ) {
if ( err ) {
if ( err ) {
Env . pinsLoaded = true ;
Env . Log . error ( "LOAD_CHANNEL_PINS" , err ) ;
Env . Log . error ( "LOAD_CHANNEL_PINS" , err ) ;
// FIXME not sure what should be done here instead
Env . pinnedPads = { } ;
Env . evPinnedPadsReady . fire ( ) ;
return ;
return ;
}
}
Env . pinsLoaded = true ;
Env . pinnedPads = data ;
answerNoConclusively( Env ) ;
Env . evPinnedPadsReady . fire ( ) ;
runRemainingDeferred( Env ) ;
} , {
} , {
pinPath : Env . paths . pin ,
pinPath : Env . paths . pin ,
handler : handler ,
pinned : Env . pinnedPads ,
workers : Env . pinWorkers ,
} ) ;
} ) ;
} ;
} ;
Pinning . isChannelPinned = function ( Env , channel , cb ) {
const deferResponse = function ( Env , channel , cb ) {
Env . evPinnedPadsReady . reg ( ( ) => {
const pending = Env . pendingPinInquiries ;
if ( Env . pinnedPads [ channel ] && Object . keys ( Env . pinnedPads [ channel ] ) . length ) { // FIXME 'Object.keys' here is overkill. We only need to know that it isn't empty
( pending [ channel ] = pending [ channel ] || [ ] ) . push ( cb ) ;
cb ( void 0 , true ) ;
} else {
delete Env . pinnedPads [ channel ] ;
cb ( void 0 , false ) ;
}
} ) ;
} ;
} ;
Pinning . isChannelPinned = function ( Env , channel , cb ) {
// if the pins are fully loaded then you can answer yes/no definitively
if ( Env . pinsLoaded ) {
return void cb ( void 0 , ! isEmpty ( Env . pinnedPads [ channel ] ) ) ;
}
// you may already know that a channel is pinned
// even if you're still loading. answer immediately if so
if ( ! isEmpty ( Env . pinnedPads [ channel ] ) ) { return cb ( void 0 , true ) ; }
// if you're still loading them then can answer 'yes' as soon
// as you learn that one account has pinned a file.
// negative responses have to wait until the end
deferResponse ( Env , channel , cb ) ;
} ;