03691fe381a86cfd8964d22a3efb678dc2cd7a1c
[platform/core/api/webapi-plugins.git] / src / filesystem / js / common.js
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 var privUtils_ = xwalk.utils;
18 var privilege_ = privUtils_.privilege;
19 var type_ = privUtils_.type;
20 var converter_ = privUtils_.converter;
21 var validator_ = privUtils_.validator;
22 var types_ = validator_.Types;
23 var native_ = new xwalk.utils.NativeManager(extension);
24
25 function SetReadOnlyProperty(obj, n, v) {
26     Object.defineProperty(obj, n, { value: v, writable: false });
27 }
28
29 var FileSystemStorageType = { INTERNAL: 'INTERNAL', EXTERNAL: 'EXTERNAL' };
30
31 var FileSystemStorageState = {
32     MOUNTED: 'MOUNTED',
33     REMOVED: 'REMOVED',
34     UNMOUNTABLE: 'UNMOUNTABLE'
35 };
36
37 var FileMode = { a: 'a', r: 'r', rw: 'rw', rwo: 'rwo', w: 'w' };
38
39 var BaseSeekPosition = { BEGIN: 'BEGIN', CURRENT: 'CURRENT', END: 'END' };
40
41 var tizen24home = '/opt/usr/media';
42
43 // this variable need to match same variable in
44 // common/filesystem/filesystem_provider_storage.cc
45 var kVirtualRootImages = 'images';
46
47 var commonFS_ = (function() {
48     var cacheReady = false;
49     var listenerRegistered = false;
50     var cacheVirtualToReal = {};
51     var cacheStorages = [];
52     var uriPrefix = 'file://';
53     // special condition for previous versions paths
54     // (global paths usage issue workaround)
55     var isAppForEarlierVersion = privUtils_.isAppVersionEarlierThan('3.0');
56     var homeDir = undefined;
57
58     function clearCache() {
59         cacheVirtualToReal = {};
60         cacheStorages = [];
61         cacheReady = false;
62     }
63
64     // initalize home directory for correct mapping global paths from tizen 2.4
65     // (global paths usage issue workaround)
66     function initHomeDir(aPath) {
67         if (homeDir || !isAppForEarlierVersion) {
68             return;
69         }
70         var imagesPath = cacheVirtualToReal[kVirtualRootImages].path;
71
72         if (imagesPath[imagesPath.length - 1] === '/') {
73             homeDir = imagesPath
74                 .split('/')
75                 .slice(0, -2)
76                 .join('/');
77         } else {
78             homeDir = imagesPath
79                 .split('/')
80                 .slice(0, -1)
81                 .join('/');
82         }
83     }
84
85     function initCache() {
86         if (cacheReady) {
87             return;
88         }
89         var result = native_.callSync('FilesystemFetchAllStorages', {});
90         if (native_.isFailure(result)) {
91             throw native_.getErrorObject(result);
92         }
93         var virtualRoots = native_.getResultObject(result);
94
95         for (var i = 0; i < virtualRoots.length; ++i) {
96             cacheVirtualToReal[virtualRoots[i].name] = {
97                 path: virtualRoots[i].path,
98                 label: virtualRoots[i].name,
99                 type: virtualRoots[i].type,
100                 state: virtualRoots[i].state
101             };
102         }
103         // initalize home directory for correct mapping global paths from tizen 2.4
104         // (global paths usage issue workaround)
105         initHomeDir();
106
107         var result = native_.callSync('FileSystemManagerFetchStorages', {});
108         if (native_.isFailure(result)) {
109             throw native_.getErrorObject(result);
110         }
111
112         var storages = native_.getResultObject(result);
113         for (var i = 0; i < storages.length; ++i) {
114             cacheStorages.push({
115                 path: storages[i].path,
116                 label: storages[i].name,
117                 type: storages[i].type,
118                 state: storages[i].state,
119                 storage_id: storages[i].storage_id
120             });
121         }
122
123         if (!listenerRegistered) {
124             try {
125                 tizen.filesystem.addStorageStateChangeListener(function() {
126                     clearCache();
127                 });
128                 listenerRegistered = true;
129             } catch (e) {
130                 privUtils_.log(
131                     'Failed to register storage change listener, ' +
132                         'storage information may be corrupted: ' +
133                         e.message
134                 );
135             }
136         }
137
138         cacheReady = true;
139     }
140
141     function setVirtualPath(name, path, type, state) {
142       initCache();
143       cacheVirtualToReal[name] = { path: path, label: name, type: type, state: state };
144       console.log('name : ' + name + ', setVirtualPath : ' + cacheVirtualToReal[name]);
145     }
146
147     function mergeMultipleSlashes(str) {
148         var retStr = str.replace(/(^(file\:\/\/\/)|^(file\:\/\/)|\/)\/{0,}/g, '$1');
149         return retStr;
150     }
151
152     function removeDotsFromPath(str) {
153         if (str === undefined) {
154             return str;
155         }
156
157         var _pathTokens = str.split('/');
158         var _correctDir = [];
159         var _fileRealPath = _pathTokens[0];
160         _correctDir.push(_pathTokens[0]);
161         for (var i = 1; i < _pathTokens.length; ++i) {
162             if (_pathTokens[i] == '..') {
163                 if (_fileRealPath == '') {
164                     _fileRealPath = undefined;
165                     break;
166                 }
167                 var _lastDir = _correctDir.pop();
168                 _fileRealPath = _fileRealPath.substring(
169                     0,
170                     _fileRealPath.length - _lastDir.length - 1
171                 );
172             } else if (_pathTokens[i] != '.') {
173                 _fileRealPath += '/' + _pathTokens[i];
174                 _correctDir.push(_pathTokens[i]);
175             }
176         }
177         return _fileRealPath;
178     }
179
180     function checkPathWithoutDots(aPath) {
181         if (-1 !== aPath.indexOf('/../')) {
182             return false;
183         }
184         if (-1 !== aPath.indexOf('/./')) {
185             return false;
186         }
187         // check if path ends with '/.' or '/..'
188         if (aPath.match(/\/\.\.?$/)) {
189             return false;
190         }
191         // check if path starts with './' or '../'
192         if (aPath.match(/^\.\.?\//)) {
193             return false;
194         }
195         return true;
196     }
197
198     function convertForEarlierVersionPath(aPath) {
199         if (isAppForEarlierVersion) {
200             if (aPath && aPath.indexOf(tizen24home) === 0) {
201                 privUtils_.log('Converting 2.4 style path to 3.0 pattern');
202                 aPath = homeDir + aPath.substr(tizen24home.length);
203             }
204         }
205         return aPath;
206     }
207
208     function toRealPath(aPath) {
209         var _fileRealPath = '';
210
211         aPath = mergeMultipleSlashes(aPath);
212
213         if (aPath.indexOf(uriPrefix) === 0) {
214             _fileRealPath = aPath.substr(uriPrefix.length);
215         } else if (aPath[0] !== '/') {
216             // virtual path
217             initCache();
218
219             var _pathTokens = aPath.split('/');
220
221             if (cacheVirtualToReal[_pathTokens[0]]) {
222                 _fileRealPath = cacheVirtualToReal[_pathTokens[0]].path;
223                 for (var i = 1; i < _pathTokens.length; ++i) {
224                     _fileRealPath += '/' + _pathTokens[i];
225                 }
226             } else {
227                 // If path token is not present in cache then it is invalid
228                 _fileRealPath = undefined;
229                 // check storages
230                 for (var j = 0; j < cacheStorages.length; ++j) {
231                     if (cacheStorages[j].label === _pathTokens[0]) {
232                         _fileRealPath = cacheStorages[j].path;
233                         for (var i = 1; i < _pathTokens.length; ++i) {
234                             _fileRealPath += '/' + _pathTokens[i];
235                         }
236                         break;
237                     }
238                 }
239             }
240         } else {
241             _fileRealPath = aPath;
242         }
243         // removeDotsFromPath execution here, results with '.' and '..' beeing
244         // supported in paths, next methods throw an error when getting argument
245         // with '.' or '..' in it
246         // (see commonFS_.checkPathWithoutDots() method)
247         _fileRealPath = removeDotsFromPath(_fileRealPath);
248         // convert path to be compatibile with previous version of Tizen
249         // (global paths usage issue workaround)
250         _fileRealPath = convertForEarlierVersionPath(_fileRealPath);
251         // if path is valid try to cut last '/' if it is present
252         if (_fileRealPath) {
253             _fileRealPath = mergeMultipleSlashes(_fileRealPath);
254         }
255         return _fileRealPath;
256     }
257
258     function toVirtualPath(aPath) {
259         aPath = mergeMultipleSlashes(aPath);
260         var _virtualPath = aPath;
261
262         if (_virtualPath.indexOf(uriPrefix) === 0) {
263             _virtualPath = _virtualPath.substr(uriPrefix.length);
264         }
265
266         initCache();
267         // find virtual root with longest path
268         var foundLength = 0;
269         var foundVirtualRoot;
270         var foundVirtualPath;
271         for (var virtual_root in cacheVirtualToReal) {
272             var real_root_path = cacheVirtualToReal[virtual_root].path;
273             if (_virtualPath.indexOf(real_root_path, 0) === 0) {
274                 var currentLength = real_root_path.length;
275                 if (currentLength > foundLength) {
276                     foundLength = currentLength;
277                     foundVirtualRoot = virtual_root;
278                     foundVirtualPath = real_root_path;
279                 }
280             }
281         }
282         if (foundLength != 0) {
283             return _virtualPath.replace(foundVirtualPath, foundVirtualRoot);
284         }
285         return _virtualPath;
286     }
287
288     function getFileInfo(aStatObj, secondIter, aMode) {
289         var _result = {},
290             _pathTokens,
291             _fileParentPath = '',
292             i;
293         var aPath = toVirtualPath(aStatObj.path);
294
295         _result.readOnly = aStatObj.readOnly;
296         _result.isFile = aStatObj.isFile;
297         _result.isDirectory = aStatObj.isDirectory;
298         _result.created = new Date(aStatObj.ctime * 1000);
299         _result.modified = new Date(aStatObj.mtime * 1000);
300         _result.fullPath = aPath;
301         _result.fileSize = aStatObj.size;
302         _result.mode = aMode;
303         if (_result.isDirectory) {
304             try {
305                 _result.length = aStatObj.nlink;
306             } catch (err) {
307                 _result.length = 0;
308             }
309         } else {
310             _result.length = undefined;
311         }
312
313         _pathTokens = aPath.split('/');
314         if (_pathTokens.length > 1) {
315             var last = _pathTokens.length - 1;
316             var lastToken = '';
317             if (_pathTokens[last] === '') {
318                 // 'abc/d/e/' case with trailing '/' sign
319                 last = _pathTokens.length - 2;
320                 lastToken = '/';
321             }
322             for (i = 0; i < last; ++i) {
323                 _fileParentPath += _pathTokens[i] + '/';
324             }
325             if (last > 0) {
326                 _result.path = _fileParentPath;
327                 _result.name = secondIter
328                     ? _pathTokens[last]
329                     : _pathTokens[last] + lastToken;
330                 _result.parent = secondIter ? null : _fileParentPath;
331             } else {
332                 // '/' dir case
333                 _result.path = _pathTokens[last] + lastToken;
334                 _result.name = '';
335                 _result.parent = secondIter ? null : _fileParentPath;
336             }
337         } else {
338             _result.parent = null;
339             _result.path = aPath;
340             _result.name = '';
341         }
342         return _result;
343     }
344
345     function isLocationAllowed(aPath) {
346         if (!aPath) {
347             return false;
348         }
349         initCache();
350         if (aPath.indexOf(cacheVirtualToReal.ringtones.path) === 0) {
351             return false;
352         }
353         if (aPath.indexOf(cacheVirtualToReal['wgt-package'].path) === 0) {
354             return false;
355         }
356
357         return true;
358     }
359
360     function toCanonicalPath(path) {
361         var result = native_.callSync('FileSystemManagerGetCanonicalPath', {
362             path: path
363         });
364         if (native_.isFailure(result)) {
365             throw native_.getErrorObject(result);
366         }
367
368         return native_.getResultObject(result);
369     }
370
371     function f_isSubDir(fullPathToCheck, fullPath) {
372         var fullCanonicalPathToCheck = toCanonicalPath(toRealPath(fullPathToCheck));
373         var fullCanonicalPath = toCanonicalPath(toRealPath(fullPath));
374
375         if (fullCanonicalPathToCheck === fullCanonicalPath) {
376             return false;
377         }
378
379         return fullCanonicalPathToCheck.indexOf(fullCanonicalPath) === 0;
380     }
381
382     function f_isCorrectRelativePath(relativePath) {
383         return (
384             0 !== relativePath.indexOf('/') &&
385             0 !== relativePath.indexOf('\\') &&
386             -1 === relativePath.indexOf('?') &&
387             -1 === relativePath.indexOf('*') &&
388             -1 === relativePath.indexOf(':') &&
389             -1 === relativePath.indexOf('"') &&
390             -1 === relativePath.indexOf('<') &&
391             -1 === relativePath.indexOf('>')
392         );
393     }
394
395     function cloneStorage(storage) {
396         return { label: storage.label, type: storage.type, state: storage.state };
397     }
398
399     function getStorage(label) {
400         initCache();
401         for (var i = 0; i < cacheStorages.length; ++i) {
402             if (cacheStorages[i].label === label) {
403                 return cloneStorage(cacheStorages[i]);
404             }
405         }
406
407         for (var key in cacheVirtualToReal) {
408             if (cacheVirtualToReal.hasOwnProperty(key)) {
409                 if (cacheVirtualToReal[key].label === label) {
410                     return cloneStorage(cacheVirtualToReal[key]);
411                 }
412             }
413         }
414
415         return null;
416     }
417
418     function getAllStorages() {
419         var ret = [];
420         initCache();
421         for (var i = 0; i < cacheStorages.length; ++i) {
422             ret.push(cloneStorage(cacheStorages[i]));
423         }
424
425         for (var key in cacheVirtualToReal) {
426             if (cacheVirtualToReal.hasOwnProperty(key)) {
427                 var found = false;
428                 for (var i = 0; i < cacheStorages.length; ++i) {
429                     if (key === ret[i].label) {
430                         found = true;
431                         break;
432                     }
433                 }
434                 if (found === false) {
435                     ret.push(cloneStorage(cacheVirtualToReal[key]));
436                 }
437             }
438         }
439
440         return ret;
441     }
442
443     return {
444         clearCache: clearCache,
445         checkPathWithoutDots: checkPathWithoutDots,
446         toRealPath: toRealPath,
447         toVirtualPath: toVirtualPath,
448         getFileInfo: getFileInfo,
449         isLocationAllowed: isLocationAllowed,
450         f_isSubDir: f_isSubDir,
451         f_isCorrectRelativePath: f_isCorrectRelativePath,
452         getStorage: getStorage,
453         getAllStorages: getAllStorages,
454         mergeMultipleSlashes: mergeMultipleSlashes,
455         setVirtualPath : setVirtualPath
456     };
457 })();