access: update access lists to use new juci ubus object paths.
[juci.git] / juci / src / js / rpc.js
1 //! Author: Martin K. Schröder <mkschreder.uk@gmail.com>
2
3 (function(scope){
4         var RPC_HOST = ""; //(($config.rpc.host)?$config.rpc.host:"")
5         var RPC_DEFAULT_SESSION_ID = "00000000000000000000000000000000"; 
6         var RPC_SESSION_ID = scope.localStorage.getItem("sid")||RPC_DEFAULT_SESSION_ID; 
7         var RPC_CACHE = {}; 
8         
9         var gettext = function(text){ return text; }; 
10         
11         // TODO: figure out a way to automatically fill out all rpc calls
12         var default_calls = [
13                 "session.access", 
14                 "session.login", 
15                 "local.features", 
16                 "local.set_rpc_host"
17         ]; 
18         
19         function rpc_request(type, namespace, method, data){
20                 var sid = ""; 
21                 var deferred = $.Deferred(); 
22                 
23                 // check if the request has been made only recently with same parameters
24                 var key = namespace+method+JSON.stringify(data); 
25                 if(!RPC_CACHE[key]){
26                         RPC_CACHE[key] = {}; 
27                 }
28                 //if(RPC_CACHE[key].time && ((new Date()).getTime() - RPC_CACHE[key].time.getTime()) < 3000){
29                 // if this request with same parameters is already in progress then just return the existing promise 
30                 if(RPC_CACHE[key].deferred && RPC_CACHE[key].deferred.state() == "pending"){
31                         return RPC_CACHE[key].deferred.promise(); 
32                 } else {
33                         RPC_CACHE[key].deferred = $.Deferred(); 
34                 } 
35                 // setup default rpcs
36                 $.jsonRPC.withOptions({
37                         namespace: "", 
38                         endPoint: RPC_HOST+"/ubus"
39                 }, function(){   
40                         //var sid = "00000000000000000000000000000000"; 
41                         //if($rootScope.sid) sid = $rootScope.sid; 
42                         //data.ubus_rpc_session = sid;  
43                         this.request(type, {
44                                 params: [ RPC_SESSION_ID, namespace, method, data],
45                                 success: function(result){
46                                         //console.log("SID: "+sid + " :: "+ JSON.stringify(result)); 
47                                         if(type == "call" && result && result.result) {
48                                                 // TODO: modify all rpc UCI services so that they ALWAYS return at least 
49                                                 // an empty json object. Otherwise we have no way to differentiate success 
50                                                 // from failure of a request. This has to be done on the host side. 
51                                                 if(result.result[0] != 0){ // || result.result[1] == undefined) {
52                                                         function _errstr(error){
53                                                                 switch(error){
54                                                                         case 0: return gettext("OK"); 
55                                                                         case 1: return gettext("Invalid command"); 
56                                                                         case 2: return gettext("Invalid parameters"); 
57                                                                         case 3: return gettext("Method not found"); 
58                                                                         case 4: return gettext("Object not found"); 
59                                                                         case 5: return gettext("No data"); 
60                                                                         case 6: return gettext("Access denied"); 
61                                                                         case 7: return gettext("Timed out"); 
62                                                                         case 8: return gettext("Not supported"); 
63                                                                         case 9: return gettext("Unknown error"); 
64                                                                         case 10: return gettext("Connection failed"); 
65                                                                         default: return gettext("RPC error #")+result.result[0]+": "+result.result[1]; 
66                                                                 }
67                                                         }
68                                                         console.log("RPC succeeded ("+namespace+"."+method+"), but returned error: "+JSON.stringify(result)+": "+_errstr(result.result[0]));
69                                                         RPC_CACHE[key].deferred.reject(_errstr(result.result[0])); 
70                                                 } else {
71                                                         // put the data into cache
72                                                         RPC_CACHE[key].time = new Date();
73                                                         RPC_CACHE[key].data = result.result[1];
74                                                         RPC_CACHE[key].deferred.resolve(result.result[1]);
75                                                 }
76                                         } else if(type == "list" && result && result.result){
77                                                 if((typeof result.result) == "object")
78                                                         RPC_CACHE[key].deferred.resolve(result.result); 
79                                                 else 
80                                                         RPC_CACHE[key].deferred.reject(result.result[1]); // for etimeout [1, "ETIMEOUT"]
81                                         } else {
82                                                 RPC_CACHE[key].deferred.reject(); 
83                                         }
84                                 }, 
85                                 error: function(result){
86                                         console.error("RPC error ("+namespace+"."+method+"): "+JSON.stringify(result));
87                                         if(result && result.error){
88                                                 RPC_CACHE[key].deferred.reject(result.error);  
89                                                 //$rootScope.$broadcast("error", result.error.message); 
90                                         }
91                                 }
92                         })
93                 });
94                 return RPC_CACHE[key].deferred.promise(); 
95         }
96         
97         /*
98         function RPCSession(){
99                 var saved_sid = scope.localStorage.getItem("sid");
100                 var default_sid = "00000000000000000000000000000000";  
101                 if(saved_sid){
102                         //$rpc.$sid(saved_sid); 
103                 } 
104                 
105                 this.sid = (saved_sid)?saved_sid:default_sid; 
106                 
107                 this.data = {}; 
108                 this.isLoggedIn = function(){
109                         return this._loggedIn; 
110                 }, 
111                 this.$init = function() {
112                         var self = this; 
113                         var deferred = $.Deferred(); 
114                         console.log("Checking session key with server: "+saved_sid); 
115                         $rpc.$authenticate().done(function(){
116                                 self._loggedIn = true; 
117                                 deferred.resolve(); 
118                         }).fail(function err(result){
119                                 self.sid = default_sid; 
120                                 scope.localStorage.setItem("sid", self.sid); 
121                                 deferred.reject(); 
122                         }); 
123                         return deferred.promise(); 
124                 };  
125                 this.login = function(obj){
126                         var self = this; 
127                         var deferred  = $.Deferred(); 
128                         // TODO: remove $session completely and use $rpc.$session instead!
129                         $rpc.$login({
130                                 "username": obj.username, 
131                                 "password": obj.password
132                         }).done(function(result){
133                                 self.sid = result.ubus_rpc_session;
134                                 self.data = result.data; 
135                                 self._loggedIn = true; 
136                                 scope.localStorage.setItem("sid", self.sid); 
137                                 //if(result && result.acls && result.acls.ubus) setupUbusRPC(result.acls.ubus); 
138                                 deferred.resolve(self.sid); 
139                         }).fail(function(result){
140                                 deferred.reject(result); 
141                         }); 
142                         return deferred.promise(); 
143                 }; 
144                 this.logout = function(){
145                         var deferred = $.Deferred(); 
146                         var self = this; 
147                         $rpc.session.destroy().done(function(){
148                                 self.data = {}; 
149                                 self._loggedIn = false; 
150                                 deferred.resolve(); 
151                         }).fail(function(){
152                                 deferred.reject(); 
153                         }); 
154                         return deferred.promise(); 
155                 }
156         }*/
157         
158         var rpc = {
159                 $sid: function(sid){
160                         if(sid) RPC_SESSION_ID = sid; 
161                         else return RPC_SESSION_ID; 
162                 }, 
163                 $isLoggedIn: function(){
164                         return RPC_SESSION_ID !== RPC_DEFAULT_SESSION_ID; 
165                 }, 
166                 $authenticate: function(){
167                         var self = this; 
168                         var deferred  = $.Deferred(); 
169                                         
170                         self.session.access({
171                                 "keys": ""
172                         }).done(function(result){
173         if(!("username" in (result.data||{}))) {
174                                         // username must be returned in the response. If it is not returned then rpcd is of wrong version. 
175                                         //alert(gettext("You have been logged out due to inactivity")); 
176                                         RPC_SESSION_ID = RPC_DEFAULT_SESSION_ID; // reset sid to 000..
177                                         scope.localStorage.setItem("sid", RPC_SESSION_ID); 
178                                         deferred.reject(); 
179                                 } else {
180                                         self.$session = result; 
181                                         if(!("data" in self.$session)) self.$session.data = {}; 
182                                         //console.log("Session: Loggedin! "); 
183                                         deferred.resolve(result); 
184                                 }  
185                         }).fail(function err(result){
186                                 RPC_SESSION_ID = RPC_DEFAULT_SESSION_ID; 
187                                 console.error("Session access call failed: you will be logged out!"); 
188                                 deferred.reject(); 
189                         }); 
190                         return deferred.promise(); 
191                 }, 
192                 $login: function(opts){
193                         var self = this; 
194                         var deferred  = $.Deferred(); 
195                         
196                         self.session.login({
197                                 "username": opts.username, 
198                                 "password": opts.password
199                         }).done(function(result){
200                                 RPC_SESSION_ID = result.ubus_rpc_session;
201                                 scope.localStorage.setItem("sid", RPC_SESSION_ID); 
202                                 self.$session = result; 
203                                 //JUCI.localStorage.setItem("sid", self.sid); 
204                                 //if(result && result.acls && result.acls.ubus) setupUbusRPC(result.acls.ubus); 
205                                 deferred.resolve(self.sid); 
206                         }).fail(function(result){
207                                 deferred.reject(result); 
208                         }); 
209                         return deferred.promise(); 
210                 },
211                 $logout: function(){
212                         var deferred = $.Deferred(); 
213                         var self = this; 
214                         self.session.destroy().done(function(){
215                                 RPC_SESSION_ID = RPC_DEFAULT_SESSION_ID; // reset sid to 000..
216                                 scope.localStorage.setItem("sid", RPC_SESSION_ID); 
217                                 deferred.resolve(); 
218                         }).fail(function(){
219                                 deferred.reject(); 
220                         }); 
221                         return deferred.promise(); 
222                 },
223                 $register: function(object, method){
224                         console.log("registering: "+object+", method: "+method); 
225                         if(!object || !method) return; 
226                         var self = this; 
227                         function _find(path, method, obj){
228                                 if(!obj.hasOwnProperty(path[0])){
229                                         obj[path[0]] = {}; 
230                                 }
231                                 if(!path.length) {
232                                         (function(object, method){
233                                                 // create the rpc method
234                                                 obj[method] = function(data){
235                                                         if(!data) data = { }; 
236                                                         return rpc_request("call", object, method, data); 
237                                                 }
238                                         })(object, method); 
239                                 } else {
240                                         var child = path[0]; 
241                                         path.shift(); 
242                                         _find(path, method, obj[child]); 
243                                 }
244                         }
245                         // support new slash paths /foo/bar..
246                         var npath = object; 
247                         if(object.startsWith("/")) npath = object.substring(1); 
248                         _find(npath.split(/[\.\/]/), method, self); 
249                 }, 
250                 $init: function(host){
251                         var self = this; 
252                         if(host) {
253                                 if(host.host) RPC_HOST = host.host;
254                         } 
255                         console.log("Init UBUS -> "+RPC_HOST); 
256                         var deferred = $.Deferred(); 
257                         default_calls.map(function(x){ self.$register(x); }); 
258                         // request list of all methods and construct rpc object containing all of the methods in javascript. 
259                         rpc_request("list", "*", "", {}).done(function(result){
260                                 //alert(JSON.stringify(result)); 
261                                 Object.keys(result).map(function(obj){
262                                         Object.keys(result[obj]).map(function(method){
263                                                 self.$register(obj, method); 
264                                         }); 
265                                 }); 
266                                 deferred.resolve(); 
267                         }).fail(function(){
268                                 deferred.reject(); 
269                         }); 
270                         return deferred.promise(); 
271                 }
272         }; 
273         
274         scope.UBUS = scope.$rpc = rpc; 
275         
276 })(typeof exports === 'undefined'? this : global); 
277