e84fb2aca264d6a5b8e19c546853e3054f999006
[platform/core/api/cordova-plugins.git] / src / lib / plugins / cordova-plugin-file-transfer / tizen / FileTransfer.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 // TODO: remove when added to public cordova repository -> begin
18 var plugin_name = 'cordova-plugin-file-transfer.tizen.FileTransfer';
19
20 cordova.define(plugin_name, function(require, exports, module) {
21 // TODO: remove -> end
22
23 function getParentPath(filePath) {
24   var pos = filePath.lastIndexOf('/');
25   return filePath.substring(0, pos + 1);
26 }
27
28 function getFileName(filePath) {
29   var pos = filePath.lastIndexOf('/');
30   return filePath.substring(pos + 1);
31 }
32
33 function TizenErrCodeToErrCode(err_code) {
34   switch (err_code) {
35     case WebAPIException.NOT_FOUND_ERR:
36       return FileTransferError.FILE_NOT_FOUND_ERR;
37
38     case WebAPIException.URL_MISMATCH_ERR:
39       return FileTransferError.INVALID_URL_ERR;
40
41     case WebAPIException.NETWORK_ERR:
42       return FileTransferError.CONNECTION_ERR;
43
44     case WebAPIException.ABORT_ERR:
45       return FileTransferError.ABORT_ERR;
46
47     default:
48       return FileTransferError.NOT_MODIFIED_ERR;
49   }
50 }
51
52 function FileErrorCodeToErrCode(err_code) {
53   switch(err_code) {
54     case FileError.SECURITY_ERR:
55       return FileTransferError.ABORT_ERR;
56     default:
57       return FileTransferError.FILE_NOT_FOUND_ERR;
58   }
59 }
60
61 function checkURL(url) {
62   return url.indexOf(' ') === -1;
63 }
64
65 var uploads = {};
66 var downloads = {};
67
68 var filePrefix = 'file://';
69
70 exports = {
71   upload: function(successCallback, errorCallback, args) {
72     var filePath = args[0],
73         server = args[1],
74         fileKey = args[2] || 'file',
75         fileName = args[3] || 'image.jpg',
76         mimeType = args[4] || 'image/jpeg',
77         params = args[5],
78         trustAllHosts = args[6], // not used
79         chunkedMode = args[7],
80         headers = args[8],
81         id = args[9],
82         httpMethod = args[10] || 'POST';
83
84     if (0 !== filePath.indexOf(filePrefix)) {
85       filePath = filePrefix + filePath;
86     }
87
88     var fail = function(code, status, response) {
89       uploads[id] && delete uploads[id];
90       var error = new FileTransferError(code, filePath, server, status, response);
91       errorCallback && errorCallback(error);
92     };
93
94     if (!checkURL(server)) {
95       fail(FileTransferError.INVALID_URL_ERR);
96       return;
97     }
98
99     function successCB(entry) {
100       if (entry.isFile) {
101         entry.file(function(file) {
102           function uploadFile(blobFile) {
103             var fd = new FormData();
104
105             fd.append(fileKey, blobFile, fileName);
106
107             for (var prop in params) {
108               if(params.hasOwnProperty(prop)) {
109                  fd.append(prop, params[prop]);
110               }
111             }
112             var xhr = uploads[id] = new XMLHttpRequest();
113
114             xhr.open(httpMethod, server);
115
116             // Fill XHR headers
117             for (var header in headers) {
118               if (headers.hasOwnProperty(header)) {
119                 xhr.setRequestHeader(header, headers[header]);
120               }
121             }
122
123             xhr.onload = function(evt) {
124               if (xhr.status === 200) {
125                 uploads[id] && delete uploads[id];
126                 successCallback({
127                   bytesSent: file.size,
128                   responseCode: xhr.status,
129                   response: xhr.response
130                 });
131               } else if (xhr.status === 404) {
132                 fail(FileTransferError.INVALID_URL_ERR, this.status, this.response);
133               } else {
134                 fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
135               }
136             };
137
138             xhr.ontimeout = function(evt) {
139               fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
140             };
141
142             xhr.onerror = function() {
143               fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
144             };
145
146             xhr.onabort = function () {
147               fail(FileTransferError.ABORT_ERR, this.status, this.response);
148             };
149
150             xhr.upload.onprogress = function (e) {
151               successCallback(e);
152             };
153
154             xhr.send(fd);
155
156             // Special case when transfer already aborted, but XHR isn't sent.
157             // In this case XHR won't fire an abort event, so we need to check if transfers record
158             // isn't deleted by filetransfer.abort and if so, call XHR's abort method again
159             if (!uploads[id]) {
160               xhr.abort();
161             }
162           }
163
164           uploadFile(file);
165
166         }, function(error) {
167           fail(FileTransferError.CONNECTION_ERR);
168         });
169       }
170     }
171
172     function errorCB(error) {
173       fail(FileErrorCodeToErrCode(error.code));
174     }
175
176     resolveLocalFileSystemURL(filePath, successCB, errorCB);
177   },
178   download: function(successCallback, errorCallback, args) {
179     var url = args[0],
180         filePath = args[1],
181         trustAllHosts = args[2],  // not used
182         id = args[3],
183         headers = args[4];
184
185     if (!checkURL(url)) {
186       errorCallback(new FileTransferError(FileTransferError.INVALID_URL_ERR, url, filePath));
187       return;
188     }
189
190     var dirPath = getParentPath(filePath);
191     var fileName = getFileName(filePath);
192
193     var xhr = downloads[id] = new XMLHttpRequest();
194
195     function fail(code, body) {
196       delete downloads[id];
197       errorCallback(new FileTransferError(code,
198                                           url,
199                                           filePath,
200                                           xhr.status,
201                                           body,
202                                           null));
203     }
204
205     xhr.addEventListener('progress', function (evt) {
206       successCallback(evt);
207     });
208
209     xhr.addEventListener('abort', function (evt) {
210       fail(FileTransferError.ABORT_ERR, xhr.response);
211     });
212
213     xhr.addEventListener('error', function (evt) {
214       fail(FileTransferError.CONNECTION_ERR, xhr.response);
215     });
216
217     xhr.addEventListener('load', function (evt) {
218       if ((xhr.status === 200 || xhr.status === 0) && xhr.response) {
219         try {
220           tizen.filesystem.resolve(dirPath, function (dir) {
221             if (dir.isFile) {
222               fail(FileTransferError.FILE_NOT_FOUND_ERR);
223               return;
224             }
225
226             function writeFile(dir) {
227               var file = dir.createFile(fileName);
228
229               file.openStream(
230                 'rw',
231                 function (stream) {
232                   stream.writeBytes(Array.prototype.slice.call(new Uint8Array(xhr.response)));
233
234                   delete downloads[id];
235
236                   resolveLocalFileSystemURL(
237                     filePath,
238                     function (fileEntry) {
239                       fileEntry.filesystemName = fileEntry.filesystem.name;
240                       successCallback(fileEntry);
241                     }, function (err) {
242                       fail(TizenErrCodeToErrCode(err.code));
243                     });
244                 }, function (err) {
245                   fail(TizenErrCodeToErrCode(err.code));
246                 }
247               );
248             }
249
250             dir.deleteFile(
251               filePath,
252               function() {
253                 writeFile(dir);
254               }, function (err) {
255                 writeFile(dir);
256               });
257
258           }, function (err) {
259             fail(TizenErrCodeToErrCode(err.code));
260           },
261           'rw');
262         } catch(e) {
263           fail(FileTransferError.ABORT_ERR);
264         }
265       } else if (xhr.status === 404) {
266         fail(FileTransferError.INVALID_URL_ERR,
267              String.fromCharCode.apply(null, new Uint8Array(xhr.response)));
268       } else {
269         fail(FileTransferError.CONNECTION_ERR,
270              String.fromCharCode.apply(null, new Uint8Array(xhr.response)));
271       }
272     });
273
274     xhr.open('GET', url, true);
275     xhr.responseType = 'arraybuffer';
276     // Fill XHR headers
277     for (var header in headers) {
278       if (headers.hasOwnProperty(header)) {
279         xhr.setRequestHeader(header, headers[header]);
280       }
281     }
282     xhr.send();
283   },
284   abort: function(successCallback, errorCallback, args) {
285     var id = args[0];
286     if (uploads[id]) {
287       uploads[id].abort();
288       delete uploads[id];
289     } else if (downloads[id]) {
290       downloads[id].abort();
291       delete downloads[id];
292     } else {
293       console.warn('Unknown file transfer ID: ' + id);
294     }
295   },
296 };
297
298 require("cordova/exec/proxy").add("FileTransfer", exports);
299
300 console.log('Loaded cordova.file-transfer API');
301
302 // TODO: remove when added to public cordova repository -> begin
303 });
304 // TODO: remove -> end