9391c3ca7fcefc14f7406f7929d556bed0c30e7a
[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(call){
224                         //console.log("registering: "+call); 
225                         if(!call) return; 
226                         var self = this; 
227                         function _find(path, obj){
228                                 if(!obj.hasOwnProperty(path[0])){
229                                         obj[path[0]] = {}; 
230                                 }
231                                 if(path.length == 1) {
232                                         var namespace = call.split("."); 
233                                         namespace.pop(); namespace = namespace.join("."); 
234                                         (function(namespace, method){
235                                                 // create the rpc method
236                                                 obj[path[0]] = function(data){
237                                                         if(!data) data = { }; 
238                                                         return rpc_request("call", namespace, method, data); 
239                                                 }
240                                         })(namespace, path[0]); 
241                                 } else {
242                                         var child = path[0]; 
243                                         path.shift(); 
244                                         _find(path, obj[child]); 
245                                 }
246                         }
247                         // support new slash paths /foo/bar..
248                         if(call.startsWith("/")) call = call.substring(1); 
249                         _find(call.split(/[\.\/]/), self); 
250                 }, 
251                 $init: function(host){
252                         var self = this; 
253                         if(host) {
254                                 if(host.host) RPC_HOST = host.host;
255                         } 
256                         console.log("Init UBUS -> "+RPC_HOST); 
257                         var deferred = $.Deferred(); 
258                         default_calls.map(function(x){ self.$register(x); }); 
259                         // request list of all methods and construct rpc object containing all of the methods in javascript. 
260                         rpc_request("list", "*", "", {}).done(function(result){
261                                 //console.log("RESULT: "+JSON.stringify(result)); 
262                                 // TODO: make this less obscure of a method :)
263                                 function _processNode(obj, cur_path){
264                                         var is_leaf = true; 
265                                         var leafs = {}; 
266                                         Object.keys(obj).map(function(x){
267                                                 if((typeof obj[x]) == "object") {
268                                                         leafs[x] = obj[x]; 
269                                                         is_leaf = false; 
270                                                 } else {
271                                                         
272                                                 }
273                                         }); 
274                                         if(is_leaf && cur_path){
275                                                 // add a new rpc call 
276                                                 //console.log("Leaf: "+namespace+", "+method); 
277                                                 self.$register(cur_path); 
278                                         } else { 
279                                                 //console.log("Processing node: "+cur_path); 
280                                                 Object.keys(leafs).map(function(x){
281                                                         var path = ((cur_path)?(cur_path+"."):"")+x; 
282                                                         _processNode(leafs[x], path); 
283                                                 }); 
284                                         }
285                                 }
286                                 _processNode(result, null); 
287                                 deferred.resolve(); 
288                         }).fail(function(){
289                                 deferred.reject(); 
290                         }); 
291                         return deferred.promise(); 
292                 }
293         }; 
294         
295         scope.UBUS = scope.$rpc = rpc; 
296         
297 })(typeof exports === 'undefined'? this : global); 
298