Source: TwisterResource.js

"use strict";

/**
 * General resource class. Inherited by all objects inside twister-lib-js.
 * @class
 */
function TwisterResource (name,scope) {

    this._type = "none";
    this._scope = scope;
    this._name = name;
	this._hasParentUser = true;
	
    this._data = null;
    this._verified = false;
    this._lastUpdate = -1;
    this._querySettings = {};
	this._revisionNumber = null;
    
    this._updateInProgress = false;
    this._activeQuerySettings = {};

}

module.exports = TwisterResource;

TwisterResource.prototype.flatten = function () {

    return { 
        lastUpdate: this._lastUpdate,
        verified: this._verified,
        querySettings: this._querySettings,
        revisionNumber: this._revisionNumber,
        name: this._name,
        data: this._data
    };

}

TwisterResource.prototype.inflate = function (flatData) {
    
    this._lastUpdate = flatData.lastUpdate;
	this._verified = flatData.verified;
	this._querySettings = flatData.querySettings;
	this._revisionNumber = flatData.revisionNumber;
    this._name = flatData.name;
    this._data = flatData.data;
	
	if (!this._verified) {this._lastUpdate=-1;}

}

TwisterResource.prototype._do =  function (cbfunc) {

    cbfunc(this);

}

TwisterResource.prototype.inCache = function () {
    return (this._lastUpdate>0);
}

/**
 * Checks whether cached resource is outdated and invokes an update if needed. Calls cbfunc on the resource when done.
 * @function
 * @param {function} cbfunc callback function
 * @param {Object} querySettings
 */
TwisterResource.prototype._checkQueryAndDo = function (cbfunc,querySettings) {
    
    if (querySettings===undefined) {querySettings={};} 
    //else {console.log(querySettings)}
    
    var Twister = this._scope;

    var thisResource = this;
        
    if (!thisResource._updateInProgress) {
        
        thisResource._activeQuerySettings = querySettings;
        thisResource._updateInProgress = true;

        var outdatedTimestamp = 0;
      
        outdatedTimestamp = Date.now()/1000 - thisResource.getQuerySetting("outdatedLimit");    

        if ( this._lastUpdate > outdatedTimestamp ){
            
            thisResource._do(cbfunc);
            
            thisResource._log("resource present in cache");
          
            thisResource._activeQuerySettings = {};
            thisResource._updateInProgress = false;

        } else {
            
            thisResource._queryAndDo(function(newresource){
                
                thisResource._do(cbfunc);
              
                thisResource._log("resource not in cahce. querying");
                
                thisResource._activeQuerySettings = {};
                thisResource._updateInProgress = false;
            
            });
            
        }

    } else {
      
        thisResource._log("update in progress "+thisResource._type+" "+thisResource._name)
        
        setTimeout(function(){
        
            thisResource._checkQueryAndDo(cbfunc,querySettings);
            
        },200);
        
    }

} 

/**
 * Retrieve currently set query setting.
 * @function
 * @param {string} settings
 */
TwisterResource.prototype.getQuerySetting = function (setting) {

	//console.log(setting,this._activeQuerySettings);
	
    var Twister = this._scope;
    
    if (setting in this._activeQuerySettings) {
        return this._activeQuerySettings[setting];
    }
    
    if (setting in this._querySettings) {
        return this._querySettings[setting];
    }
    
    //console.log(this._type,Twister._querySettingsByType)
  
    if (setting in Twister._querySettingsByType && this._type in Twister._querySettingsByType[setting]) {
        return Twister._querySettingsByType[setting][this._type];
    }
    
    if (this._hasParentUser && setting in Twister.getUser(this._name)._querySettings) {
        return Twister.getUser(this._name)._querySettings[setting];
    }
    
    if ( ("_"+setting) in Twister) {
        return Twister[("_"+setting)];
    }
    
    this._handleError({
      message:"Unknown query setting was requested.",
      code: 32051
    });

}

TwisterResource.prototype.setQuerySettings = function (settings) {

    for (var key in settings) {
		
		this._querySettings[key] = settings[key];
		
	}

}

TwisterResource.prototype._handleError = function (error) {
    
    this._updateInProgress = false;
	
    this.getQuerySetting("errorfunc").call(this,error);
    
}

TwisterResource.prototype._log = function (log) {
    
    this.getQuerySetting("logfunc").call(this,log);
    
}

TwisterResource.prototype.RPC = function (method, params, resultFunc, errorFunc) {
    
	var thisResource = this;
  
    thisResource._log("calling JSON-RPC "+method+" "+JSON.stringify(params));
	
	if (typeof errorFunc != "function") {
	
		thisResource._activeQuerySettings = errorFunc;
	
	}
    
	//this._activeQuerySettings["method"]=method;
	//this._activeQuerySettings["params"]=params;
	
	//console.log("rpc by "+this._name+" : "+method+" "+JSON.stringify(this._activeQuerySettings))
	
    if ( (typeof $ == "function") && ( typeof $.JsonRpcClient == "function") ) {
        
        var foo = new $.JsonRpcClient({ 
        ajaxUrl: this.getQuerySetting("host"),
        timeout: this.getQuerySetting("timeout")
        });
        foo.call(method, params,
            function(ret) { if(typeof resultFunc === "function") resultFunc(ret); },
            function(ret) { if(typeof errorFunc === "function" && ret != null) errorFunc(ret); }
        );
        
    } else {
                    
        var request = require('request');
        request({
          
            uri: this.getQuerySetting("host"),
            method: "POST",
            timeout: this.getQuerySetting("timeout"),
            followRedirect: true,
            maxRedirects: 10,
            body: '{"jsonrpc": "2.0", "method": "'+method+'", "params": '+JSON.stringify(params)+', "id": 0}'
          
        }, function(error, response, body) {
            
            if (error) { 
				
				error.message = "Host not reachable (http error).";
				
				thisResource._handleError(error)
			
			} else {
                var res = JSON.parse(body);
                if (res.error) {
                    thisResource._handleError(res.error);
                } else {
                    resultFunc(res.result);
                }
                
            }
            
        });
        
    }
      
}

TwisterResource.prototype.dhtget = function (args,cbfunc) {

    var Twister = this._scope;
    
    var thisResource = this;
  
    if ( Twister._activeDHTQueries < Twister._maxDHTQueries ) {
    
        Twister._activeDHTQueries++;
        
        thisResource.RPC("dhtget", args, function(res){
            
            Twister._activeDHTQueries--;
            
            thisResource._log("dhtget result: "+JSON.stringify(res));
          
            if (res[0]) {
				
				var signatureVerification = thisResource.getQuerySetting("signatureVerification");
				
                var signingUser = res[0].sig_user;
                
                if (signatureVerification!="none" 
					&& (args[2]="m" || (args[0]==signingUser) ) ) {
                  
                    thisResource._log("issuing signature verification");
                
                    if (signatureVerification=="background") { cbfunc(res); }

                    Twister.getUser(signingUser)._doPubKey(function(pubkey){

                        pubkey.verifySignature(res[0].p,res[0].sig_p,function(verified){


                            if (verified) {

                                thisResource._verified = true;
								
								if (signatureVerification=="instant") { cbfunc(res); }
								
                            } else {

                                thisResource._handleError({
                                  message: "DHT resource signature could not be verified",
                                  code: 32050
                                })

                            }

                        });

                    });
                    
                } else { 
                  thisResource._verified = true;
                  thisResource._log("no signature verification needed");
                  cbfunc(res); 
                }
                
            } else { 
              thisResource._log("dht resource is empty"); 
              cbfunc(res);
            }
            
        }, function(error) {
            
            Twister._activeDHTQueries--;
            thisResource._handleError(error);
            
        });
        
    } else {
                
        setTimeout(function(){
        
            thisResource.dhtget(args,cbfunc);
            
        },200);
    
    }

}