Merge "[Application] Fixed path of getAppSharedURI" into tizen_5.0
[platform/core/api/webapi-plugins.git] / src / archive / archive_api.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 validator_ = xwalk.utils.validator;
18 var types_ = validator_.Types;
19 var native_ = new xwalk.utils.NativeManager(extension);
20 var privUtils_ = xwalk.utils;
21
22 function CommonFS() {};
23
24 CommonFS.cacheVirtualToReal = {};
25 CommonFS.isCacheReady = false;
26 CommonFS.listenerRegistered = false;
27
28 function clearCache() {
29     CommonFS.cacheVirtualToReal = {};
30     CommonFS.isCacheReady = false;
31 }
32
33 function _initializeCache() {
34     if (CommonFS.isCacheReady) {
35         return;
36     }
37     var result = native_.callSync('Archive_fetchStorages', {});
38
39     if (native_.isFailure(result)) {
40         privUtils_.log("Exception while getting widget paths was thrown: " + native_.getErrorObject(result).message);
41         return;
42     }
43
44     result = native_.getResultObject(result);
45     for (var i = 0; i < result.length; ++i) {
46         CommonFS.cacheVirtualToReal[result[i].name] = {
47             path: result[i].path
48         };
49     }
50     CommonFS.isCacheReady = true;
51     if (!CommonFS.listenerRegistered) {
52         try {
53           tizen.filesystem.addStorageStateChangeListener(clearCache);
54           CommonFS.listenerRegistered = true;
55         } catch (e) {
56           privUtils_.log('Failed to register storage change listener, '
57               + 'storage information may be corrupted: ' + e.message);
58         }
59     }
60 }
61
62 _initializeCache();
63
64 CommonFS.toRealPath = function(aPath) {
65     var _fileRealPath = '', _uriPrefix = 'file://', i;
66     if (aPath.indexOf(_uriPrefix) === 0) {
67         _fileRealPath = aPath.substr(_uriPrefix.length);
68     } else if (aPath[0] != '/') {
69         // virtual path$
70         _initializeCache();
71         var _pathTokens = aPath.split('/');
72         if (this.cacheVirtualToReal[_pathTokens[0]]
73                 && (this.cacheVirtualToReal[_pathTokens[0]].state === undefined || this.cacheVirtualToReal[_pathTokens[0]].state === 'MOUNTED')) {
74             _fileRealPath = this.cacheVirtualToReal[_pathTokens[0]].path;
75             for (i = 1; i < _pathTokens.length; ++i) {
76                 _fileRealPath += '/' + _pathTokens[i];
77             }
78         } else {
79             _fileRealPath = aPath;
80         }
81     } else {
82         _fileRealPath = aPath;
83     }
84     privUtils_.log("REAL PATH:" + _fileRealPath);
85     if (_fileRealPath === "undefined" || _fileRealPath === "null") {
86         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR);
87     }
88     return _fileRealPath;
89 };
90
91 CommonFS.isVirtualPath = function(aPath) {
92     var root = aPath.split("/")[0];
93     _initializeCache();
94     return this.cacheVirtualToReal[root] != undefined;
95 };
96
97 /**
98  * Returns new unique opId
99  */
100 var getNextOpId = (function () {
101     var opId = 0,
102         incOpId = function () {
103             return opId++;
104         };
105     return incOpId;
106 }());
107
108 var Property = {
109     W: 1 << 0,   // WRITABLE
110     E: 1 << 1,   // ENUMERABLE
111     C: 1 << 2    // CONFIGURABLE
112 }
113
114 /**
115  * Example usage:
116  * function Messaging () {
117  *     propertyFactory_(this, 'ids', [2,3,4], Property.W | Property.E | Property.C);
118  *     propertyFactory_(this, 'name', 'Name', Property.E);
119  *     propertyFactory_(this, 'age', 25, Property.W);
120  *     propertyFactory_(this, 'something', 1);
121  *     propertyFactory_(this, 'getSomething', Property.E, {get: function(){return 100;}});
122  * }
123  * Will produce:
124  * var m = new Messaging();
125  * {
126  *     id: [2,3,4],
127  *     name: 'Name',
128  *     age: 25
129  * }
130  *
131  * m.name = 'A brand new name';
132  * privUtils_.log(m.name); // Name
133  */
134 function propertyFactory_(that, name, value, flags, options) {
135     flags = flags || 0;
136     if (options === null || typeof options !== 'object') {
137         options = {};
138     }
139     if (!(options.get) && !(options.set)) {
140         options.value = value;
141     }
142     if ((flags & Property.W) != 0) { options.writable     = true; }
143     if ((flags & Property.E) != 0) { options.enumerable   = true; }
144     if ((flags & Property.C) != 0) { options.configurable = true; }
145     Object.defineProperty(
146         that,
147         name,
148         options
149     );
150 }
151
152 function checkMode(mode, access)
153 {   if(access.indexOf(mode) == -1) {
154         throw new WebAPIException(WebAPIException.INVALID_ACCESS_ERR, 'Not allowed operation');
155     }
156 }
157
158 /**
159  * Enumeration for the compression level.
160  * @enum {string}
161  */
162 var ArchiveCompressionLevel = {
163     STORE: "STORE",
164     FAST: "FAST",
165     NORMAL: "NORMAL",
166     BEST: "BEST"
167 };
168
169 var onprogressCallbacks = {};
170 var ARCHIVE_ONPROGRESS_CALLBACK = 'ArchiveOnprogressCallback';
171
172 var ArchiveFileProgressCallback = function(msg) {
173     if (native_.isFailure(msg)) {
174         return;
175     }
176
177     var result = native_.getResultObject(msg);
178     if ('onprogress' === result.action && onprogressCallbacks.hasOwnProperty(result.opId)) {
179         onprogressCallbacks[result.opId](result.opId, result.value, result.filename);
180     }
181 };
182
183 native_.addListener(ARCHIVE_ONPROGRESS_CALLBACK, ArchiveFileProgressCallback);
184
185 /**
186  * The ArchiveFileEntry interface provides access to ArchiveFile member information and file data.
187  * This constructor is for internal use only.
188  * It should be prohibited to call this constructor by user.
189  */
190 function ArchiveFileEntry(data, priv) {
191     if (!(this instanceof ArchiveFileEntry)) {
192         return new ArchiveFileEntry(data);
193     }
194
195     if (data === null || typeof data !== 'object') {
196         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR);
197     }
198
199     propertyFactory_(this, 'name',           data.name                      || "",    Property.E);
200     propertyFactory_(this, 'size',           data.size                      || 0,     Property.E);
201     propertyFactory_(this, 'compressedSize', data.compressedSize            || 0,     Property.E);
202     propertyFactory_(this, 'modified',       new Date(data.modified * 1000) || null , Property.E);
203
204     function getHandle() {
205         if(priv.handle)
206             return priv.handle;
207         else throw new WebAPIException(WebAPIException.UNKNOWN_ERR, 'Archive is not opened');
208     }
209
210     /**
211      * Extracts ArchiveFileEntry to the given location.
212      */
213     this.extract = function () {
214         var args = validator_.validateArgs(arguments, [
215             { name: "destinationDirectory", type: types_.FILE_REFERENCE },
216             { name: "onsuccess", type: types_.FUNCTION, optional: true, nullable: true },
217             { name: "onerror", type: types_.FUNCTION, optional: true, nullable: true },
218             { name: "onprogress", type: types_.FUNCTION, optional: true, nullable: true },
219             { name: "stripName", type: types_.STRING, optional: true, nullable: true },
220             { name: "overwrite", type: types_.BOOLEAN, optional: true, nullable: true }
221         ]),
222         opId = getNextOpId();
223
224         if (!CommonFS.isVirtualPath(args.destinationDirectory)) {
225             throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
226                     "Destination directory should be virtual path or file.");
227         }
228
229         var callArgs = {
230             destinationDirectory : CommonFS.toRealPath(args.destinationDirectory),
231             stripName : args.stripName || null,
232             overwrite : args.overwrite || null,
233             opId : opId,
234             handle : getHandle(),
235             name : this.name
236         };
237
238         var callback = function(result) {
239             if (native_.isFailure(result)) {
240                 native_.callIfPossible(args.onerror, native_.getErrorObject(result));
241             } else {
242                 var ret = native_.getResultObject(result);
243                 delete onprogressCallbacks[opId];
244                 native_.callIfPossible(args.onsuccess);
245             }
246         };
247
248         if (args.onprogress) {
249             onprogressCallbacks[opId] = args.onprogress;
250         }
251
252         var result = native_.call('ArchiveFileEntry_extract', callArgs, callback);
253         if (native_.isFailure(result)) {
254             throw native_.getErrorObject(result);
255         }
256
257         return opId;
258     };
259 }
260
261
262 /**
263  * The ArchiveManager interface provides methods for global operations related to ArchiveFile.
264  */
265
266 /**
267  * ArchiveFile interface provides access to member files of the archive file.
268  * This constructor is for internal use only.
269  * It should be prohibited to call this constructor by user.
270  */
271 function ArchiveFile(data) {
272     if (!(this instanceof ArchiveFile)) {
273         return new ArchiveFile(data);
274     }
275
276     if (data === null || typeof data !== 'object') {
277         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR);
278     }
279
280     propertyFactory_(this, 'mode'            , data.mode             || "r", Property.E);
281     propertyFactory_(this, 'decompressedSize', data.decompressedSize || 0,   Property.E);
282
283     var priv ={ handle: data.handle };
284
285     function getHandle() {
286         if(priv.handle)
287             return priv.handle;
288         else throw new WebAPIException(WebAPIException.INVALID_STATE_ERR, 'ArchiveFile closed - operation not permitted');
289     }
290
291     /**
292      * Adds a new member file to ArchiveFile.
293      */
294     this.add = function () {
295         var args = validator_.validateArgs(arguments, [
296             { name: "sourceFile", type: types_.FILE_REFERENCE },
297             { name: "onsuccess", type: types_.FUNCTION, optional: true, nullable: true },
298             { name: "onerror", type: types_.FUNCTION, optional: true, nullable: true },
299             { name: "onprogress", type: types_.FUNCTION, optional: true, nullable: true },
300             { name: "options", type: types_.DICTIONARY, optional: true, nullable: true }
301         ]),
302         opId = getNextOpId();
303
304         if (!CommonFS.isVirtualPath(args.sourceFile)) {
305             throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
306                     "sourceFile should be virtual path or file.");
307         }
308
309         var optionsAttributes = ["destination", "stripSourceDirectory", "compressionLevel"],
310             options = args.options || {};
311
312         for(var i in optionsAttributes) {
313             if (!options[optionsAttributes[i]]) {
314                 options[optionsAttributes[i]] = null;
315             }
316         }
317
318         checkMode(this.mode, ["w","rw", "a"]);
319
320         var callArgs = {
321             sourceFile : CommonFS.toRealPath(args.sourceFile),
322             options : options,
323             opId : opId,
324             handle : getHandle()
325         };
326
327         var callback = function(result) {
328             if (native_.isFailure(result)) {
329                 native_.callIfPossible(args.onerror, native_.getErrorObject(result));
330             } else {
331                 delete onprogressCallbacks[opId];
332                 native_.callIfPossible(args.onsuccess);
333             }
334         };
335
336         if (args.onprogress) {
337             onprogressCallbacks[opId] = args.onprogress;
338         }
339
340         var result = native_.call('ArchiveFile_add', callArgs, callback);
341         if (native_.isFailure(result)) {
342             throw native_.getErrorObject(result);
343         }
344
345         return opId;
346     };
347
348     /**
349      * Extracts every file from this ArchiveFile to a given directory.
350      */
351     this.extractAll = function () {
352         var args = validator_.validateArgs(arguments, [
353             { name: "destinationDirectory", type: types_.FILE_REFERENCE },
354             { name: "onsuccess", type: types_.FUNCTION, optional: true, nullable: true },
355             { name: "onerror", type: types_.FUNCTION, optional: true, nullable: true },
356             { name: "onprogress", type: types_.FUNCTION, optional: true, nullable: true },
357             { name: "overwrite", type: types_.BOOLEAN, optional: true, nullable: true }
358         ]),
359         opId = getNextOpId();
360
361         if (!CommonFS.isVirtualPath(args.destinationDirectory)) {
362             throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
363                     "destinationDirectory should be virtual path or file.");
364         }
365
366         checkMode(this.mode, ["r","rw"]);
367
368         var callArgs = {
369             destinationDirectory : CommonFS.toRealPath(args.destinationDirectory),
370             overwrite : args.overwrite || null,
371             opId : opId,
372             handle : getHandle()
373         };
374
375         var callback = function(result) {
376             if (native_.isFailure(result)) {
377                 native_.callIfPossible(args.onerror, native_.getErrorObject(result));
378             } else {
379                 delete onprogressCallbacks[opId];
380                 native_.callIfPossible(args.onsuccess);
381             }
382         };
383
384         if (args.onprogress) {
385             onprogressCallbacks[opId] = args.onprogress;
386         }
387
388         var result = native_.call('ArchiveFile_extractAll', callArgs, callback);
389         if (native_.isFailure(result)) {
390             throw native_.getErrorObject(result);
391         }
392
393         return opId;
394     };
395
396     /**
397      * Retrieves information about the member files in ArchiveFile.
398      */
399     this.getEntries = function () {
400         var args = validator_.validateArgs(arguments, [
401             { name: "onsuccess", type: types_.FUNCTION },
402             { name: "onerror", type: types_.FUNCTION, optional: true, nullable: true }
403         ]),
404         opId = getNextOpId();
405
406         checkMode(this.mode, ["r","rw"]);
407
408         var callArgs = {
409             opId : opId,
410             handle : getHandle()
411         };
412
413         var callback = function(result) {
414             if (native_.isFailure(result)) {
415                 native_.callIfPossible(args.onerror, native_.getErrorObject(result));
416             } else {
417                 var entries = [];
418                 var ret = native_.getResultObject(result);
419                 ret.forEach(function (e) {
420                     entries.push(new ArchiveFileEntry(e, priv));
421                 });
422                 args.onsuccess(entries);
423           }
424         };
425
426         var result = native_.call('ArchiveFile_getEntries', callArgs, callback);
427         if (native_.isFailure(result)) {
428             throw native_.getErrorObject(result);
429         }
430
431         return opId;
432     };
433
434     /**
435      * Retrieves information about ArchiveFileEntry with the specified name in ArchiveFile.
436      */
437     this.getEntryByName = function () {
438         var args = validator_.validateArgs(arguments, [
439             { name: "name", type: types_.STRING },
440             { name: "onsuccess", type: types_.FUNCTION },
441             { name: "onerror", type: types_.FUNCTION, optional: true, nullable: true }
442         ]),
443         opId = getNextOpId();
444
445         checkMode(this.mode, ["r","rw"]);
446
447         var callArgs = {
448             name : args.name,
449             opId : opId,
450             handle : getHandle()
451         };
452
453         var callback = function(result) {
454             if (native_.isFailure(result)) {
455                 native_.callIfPossible(args.onerror, native_.getErrorObject(result));
456             } else {
457                 args.onsuccess(new ArchiveFileEntry(native_.getResultObject(result), priv));
458             }
459         };
460
461         var result = native_.call('ArchiveFile_getEntryByName', callArgs, callback);
462         if (native_.isFailure(result)) {
463             throw native_.getErrorObject(result);
464         }
465
466         return opId;
467     };
468
469     /**
470      * Closes the ArchiveFile.
471      */
472     this.close = function () {
473         var handle = priv.handle;
474         if(priv.handle) {
475             delete priv.handle;
476             var result = native_.callSync('ArchiveFile_close', {'handle': handle});
477
478             if (native_.isFailure(result)) {
479                 throw native_.getErrorObject(result);
480             }
481         }
482     };
483 }
484
485
486 var ArchiveManager = function () {
487 };
488
489 /**
490  * Opens the archive file. After this operation, it is possible to add or get files to and from the archive.
491  */
492 ArchiveManager.prototype.open = function () {
493     var args = validator_.validateArgs(arguments, [
494         { name: "file", type: types_.FILE_REFERENCE },
495         { name: "mode", type: types_.ENUM, values: ["r", "rw", "w", "a"] },
496         { name: "onsuccess", type: types_.FUNCTION },
497         { name: "onerror", type: types_.FUNCTION, optional: true, nullable: true },
498         { name: "options", type: types_.DICTIONARY, optional: true, nullable: true }
499     ]),
500     opId = getNextOpId();
501
502     var optionsAttributes = ["overwrite"],
503         options = args.options || {};
504
505     for(var i in optionsAttributes) {
506         if (!options[optionsAttributes[i]]) {
507             options[optionsAttributes[i]] = null;
508         }
509     }
510
511     if (!CommonFS.isVirtualPath(args.file)) {
512         throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR,
513                 "file should be virtual path or file.");
514     }
515
516     var callArgs = {
517         file : CommonFS.toRealPath(args.file),
518         mode : args.mode,
519         options : options,
520         opId : opId,
521     };
522
523
524     var callback = function(result) {
525         if (native_.isFailure(result)) {
526             native_.callIfPossible(args.onerror, native_.getErrorObject(result));
527         } else {
528             args.onsuccess(new ArchiveFile(native_.getResultObject(result)));
529         }
530     };
531
532     var result = native_.call('ArchiveManager_open', callArgs, callback);
533     if (native_.isFailure(result)) {
534         throw native_.getErrorObject(result);
535     }
536
537     return opId;
538 };
539
540 /**
541  * Cancels an operation with the given identifier.
542  */
543 ArchiveManager.prototype.abort = function () {
544     var args = validator_.validateArgs(arguments, [
545         { name: "opId", type: types_.LONG }
546     ]);
547
548     var result = native_.callSync('ArchiveManager_abort', {opId: args.opId});
549
550     if (native_.isFailure(result)) {
551         throw native_.getErrorObject(result);
552     }
553 };
554
555 //Exports
556 exports = new ArchiveManager();