2 // Tizen Web Device API
3 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
5 // Licensed under the Apache License, Version 2.0 (the License);
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
9 // http://www.apache.org/licenses/LICENSE-2.0
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
17 #include "ArchiveFile.h"
20 #include <PlatformException.h>
21 #include <GlobalContextManager.h>
24 #include "ArchiveManager.h"
25 #include "JSArchiveFileEntry.h"
26 #include "JSArchiveFile.h"
27 #include "ArchiveUtils.h"
28 #include "plugin_config_impl.h"
35 using namespace Common;
37 Permission::Permission(bool r, bool w, bool rw, bool a){
44 PermissionMap ArchiveFile::s_permission_map = {
45 {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_ADD,
46 Permission(NOT_ALLOWED, ALLOWED, ALLOWED, ALLOWED)},
47 {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_EXTRACT_ALL,
48 Permission(ALLOWED, NOT_ALLOWED, ALLOWED, NOT_ALLOWED)},
49 {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_GET_ENTRIES,
50 Permission(ALLOWED, NOT_ALLOWED, ALLOWED, NOT_ALLOWED)},
51 {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_GET_ENTRY_BY_NAME,
52 Permission(ALLOWED, NOT_ALLOWED, ALLOWED, NOT_ALLOWED)}
55 ArchiveFile::ArchiveFile() :
56 enable_shared_from_this<ArchiveFile>(),
57 m_decompressed_size(0),
60 m_created_as_new_empty_archive(false)
65 ArchiveFile::ArchiveFile(FileMode file_mode) :
66 enable_shared_from_this<ArchiveFile>(),
67 m_decompressed_size(0),
71 m_file_mode = file_mode;
74 ArchiveFile::~ArchiveFile()
79 LOGD("Unlinking old m_entry_map: %d ArchiveFileEntries", m_entry_map->size());
80 for(auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) {
82 it->second->setArchiveFileNonProtectPtr(NULL);
88 gboolean ArchiveFile::openTaskCompleteCB(void *data)
91 auto callback = static_cast<OperationCallbackData*>(data);
93 LOGE("callback is null");
97 JSContextRef context = callback->getContext();
98 if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
99 LOGE("context was closed");
105 if (callback->isError()) {
106 JSObjectRef errobj = JSWebAPIErrorFactory::makeErrorObject(context,
107 callback->getErrorName(),
108 callback->getErrorMessage());
109 callback->callErrorCallback(errobj);
112 JSObjectRef result = JSArchiveFile::makeJSObject(context,
113 callback->getArchiveFile());
114 callback->callSuccessCallback(result);
117 catch (const BasePlatformException &err) {
118 LOGE("%s (%s)", err.getName().c_str(), err.getMessage().c_str());
121 LOGE("Unknown error occurs");
130 gboolean ArchiveFile::callErrorCallback(void* data)
133 auto callback = static_cast<OperationCallbackData*>(data);
135 LOGE("callback is null");
139 JSContextRef context = callback->getContext();
140 if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
141 LOGE("context was closed");
148 if (callback->isError()) {
149 JSObjectRef errobj = JSWebAPIErrorFactory::makeErrorObject(context,
150 callback->getErrorName(),
151 callback->getErrorMessage());
152 callback->callErrorCallback(errobj);
155 LOGW("The success callback should be not be called in this case");
158 catch (const BasePlatformException &err) {
159 LOGE("%s (%s)", err.getName().c_str(), err.getMessage().c_str());
162 LOGE("Unknown error occurs");
171 void* ArchiveFile::taskManagerThread(void *data)
174 ArchiveFileHolder* archive_file_holder = static_cast<ArchiveFileHolder*>(data);
175 if (!archive_file_holder) {
176 LOGE("archive_file_holder is null");
180 if (!archive_file_holder->ptr){
181 LOGE("archive_file is null");
182 delete archive_file_holder;
183 archive_file_holder = NULL;
188 OperationCallbackData* callback = NULL;
189 bool call_error_callback = false;
192 std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
193 if(archive_file_holder->ptr->m_task_queue.empty()){
196 callback = archive_file_holder->ptr->m_task_queue.back().second;
198 if(callback && !callback->isCanceled()){
199 callback->executeOperation(archive_file_holder->ptr);
202 std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
203 archive_file_holder->ptr->m_task_queue.pop_back();
205 } catch (const OperationCanceledException &err) {
207 std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
208 archive_file_holder->ptr->m_task_queue.pop_back();
212 } catch (const BasePlatformException &err) {
213 LOGE("taskManagerThread fails, %s: %s", err.getName().c_str(),
214 err.getMessage().c_str());
215 callback->setError(err.getName().c_str(), err.getMessage().c_str());
216 call_error_callback = true;
218 LOGE("taskManagerThread fails");
219 callback->setError(JSWebAPIErrorFactory::UNKNOWN_ERROR, "UnknownError.");
220 call_error_callback = true;
222 if(call_error_callback) {
224 std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
225 archive_file_holder->ptr->m_task_queue.pop_back();
227 if (!g_idle_add(callErrorCallback, static_cast<void*>(callback))) {
228 LOGE("g_idle_add fails");
235 delete archive_file_holder;
236 archive_file_holder = NULL;
241 long ArchiveFile::addOperation(OperationCallbackData* callback)
243 LOGD("Entered callback type:%d", callback->getCallbackType());
245 const long operation_id =
246 ArchiveManager::getInstance().getNextOperationId(shared_from_this());
247 callback->setOperationId(operation_id);
248 callback->setArchiveFile(shared_from_this());
249 std::size_t size = 0;
251 std::lock_guard<std::mutex> lock(m_mutex);
252 m_task_queue.push_front(CallbackPair(operation_id, callback));
253 size = m_task_queue.size();
257 ArchiveFileHolder* holder = new(std::nothrow) ArchiveFileHolder();
259 LOGE("Memory allocation error");
260 throw UnknownException("Memory allocation error");
262 holder->ptr = shared_from_this();
263 if (pthread_create(&thread, NULL, taskManagerThread,
264 static_cast<void*>(holder))) {
265 LOGE("Thread creation failed");
268 throw UnknownException("Thread creation failed");
271 if (pthread_detach(thread)) {
272 LOGE("Thread detachment failed");
278 void ArchiveFile::extractAllTask(ExtractAllProgressCallback* callback)
280 Filesystem::FilePtr directory = callback->getDirectory();
283 LOGE("Directory is null");
284 throw UnknownException("Directory is null");
286 if(!directory->getNode()){
287 LOGE("Node in directory is null");
288 throw UnknownException("Node is null");
293 LOGE("File is null");
294 throw UnknownException("File is null");
296 if(!m_file->getNode()){
297 LOGE("Node in file is null");
298 throw UnknownException("Node in file is null");
302 // For explanation please see:
303 // ArchiveFile.h m_created_as_new_empty_archive description
305 if(m_file->getNode()->getSize() == 0) {
306 LOGD("Zip file: %s is empty",
307 m_file->getNode()->getPath()->getFullPath().c_str());
309 if(m_created_as_new_empty_archive) {
310 //We do not call progress callback since we do not have any ArchiveFileEntry
311 callback->callSuccessCallbackOnMainThread();
316 LOGW("m_created_as_new_empty_archive is false");
317 LOGE("Throwing InvalidStateException: File is not valid ZIP archive");
318 throw InvalidStateException("File is not valid ZIP archive");
322 UnZipPtr unzip = createUnZipObject();
323 unzip->extractAllFilesTo(directory->getNode()->getPath()->getFullPath(), callback);
326 long ArchiveFile::getEntries(GetEntriesCallbackData* callback)
330 LOGE("callback is NULL");
331 throw UnknownException("Could not get list of files in archive");
334 throwInvalidStateErrorIfArchiveFileIsClosed();
336 return addOperation(callback);
339 gboolean ArchiveFile::getEntriesTaskCompleteCB(void *data)
341 auto callback = static_cast<GetEntriesCallbackData*>(data);
343 LOGE("callback is null");
347 JSContextRef context = callback->getContext();
348 if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
349 LOGE("context was closed");
356 ArchiveFileEntryPtrMapPtr entries = callback->getEntries();
357 unsigned int size = entries->size();
359 JSObjectRef objArray[size];
361 for(auto it = entries->begin(); it != entries->end(); it++) {
362 objArray[i] = JSArchiveFileEntry::makeJSObject(context, it->second);
366 JSValueRef exception = NULL;
367 JSObjectRef jsResult = JSObjectMakeArray(context, size,
368 size > 0 ? objArray : NULL, &exception);
369 if (exception != NULL) {
370 throw Common::UnknownException(context, exception);
373 callback->callSuccessCallback(jsResult);
375 catch (const BasePlatformException &err) {
376 LOGE("%s (%s)", err.getName().c_str(), err.getMessage().c_str());
379 LOGE("Unknown error occurs");
388 long ArchiveFile::extractAll(ExtractAllProgressCallback *callback)
392 LOGE("callback is NULL");
393 throw UnknownException("Could not extract all files from archive");
396 throwInvalidStateErrorIfArchiveFileIsClosed();
398 return addOperation(callback);
401 long ArchiveFile::extractEntryTo(ExtractEntryProgressCallback* callback)
405 LOGE("callback is NULL");
406 throw UnknownException("Could not extract archive file entry");
409 // FIXME according to documentation:
410 // if archive was closed, any further operation attempt will make InvalidStateError
411 // but method extract() from ArchiveFileEntryObject is not permitted to throw above exception
413 // uncomment in case when this method have permission to throwing InvalidStateError
414 // throwInvalidStateErrorIfArchiveFileisClosed();
416 LOGE("Archive is not opened");
417 throw UnknownException("Archive is not opened");
420 return addOperation(callback);
424 long ArchiveFile::add(AddProgressCallback *callback)
428 LOGE("callback is NULL");
429 throw UnknownException("Could not add file to archive");
431 if(FileMode::READ == m_file_mode) {
432 LOGE("Trying to add file when READ access mode selected");
433 throw InvalidAccessException("Add not allowed for \"r\" access mode");
436 throwInvalidStateErrorIfArchiveFileIsClosed();
438 return addOperation(callback);
441 void ArchiveFile::close()
446 LOGD("Archive already closed");
453 long ArchiveFile::getEntryByName(GetEntryByNameCallbackData* callback)
457 LOGE("callback is NULL");
458 throw UnknownException("Could not get archive file entries by name");
461 throwInvalidStateErrorIfArchiveFileIsClosed();
463 return addOperation(callback);
466 gboolean ArchiveFile::getEntryByNameTaskCompleteCB(void *data)
468 auto callback = static_cast<GetEntryByNameCallbackData*>(data);
470 LOGE("callback is null");
474 JSContextRef context = callback->getContext();
475 if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
476 LOGE("context was closed");
482 if (callback->isError()) {
483 JSObjectRef errobj = JSWebAPIErrorFactory::makeErrorObject(context,
484 callback->getErrorName(),
485 callback->getErrorMessage());
486 callback->callErrorCallback(errobj);
489 JSObjectRef entry = JSArchiveFileEntry::makeJSObject(context,
490 callback->getFileEntry());
491 callback->callSuccessCallback(entry);
494 catch (const BasePlatformException &err) {
495 LOGE("%s (%s)", err.getName().c_str(), err.getMessage().c_str());
498 LOGE("Unknown error occurs");
507 Filesystem::FilePtr ArchiveFile::getFile() const
513 void ArchiveFile::setFile(Filesystem::FilePtr file)
519 bool ArchiveFile::isOverwrite() const
524 void ArchiveFile::setOverwrite(bool overwrite)
527 m_overwrite = overwrite;
530 unsigned long ArchiveFile::getDecompressedSize() const
533 return m_decompressed_size;
536 void ArchiveFile::setDecompressedSize(unsigned long decompressed_size)
539 m_decompressed_size = decompressed_size;
542 bool ArchiveFile::isOpen() const
548 void ArchiveFile::setIsOpen(bool is_open)
554 ArchiveFileEntryPtrMapPtr ArchiveFile::getEntryMap() const
559 void ArchiveFile::setEntryMap(ArchiveFileEntryPtrMapPtr entries)
564 LOGD("Unlinking old m_entry_map: %d ArchiveFileEntries", m_entry_map->size());
565 for(auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) {
567 it->second->setArchiveFileNonProtectPtr(NULL);
572 m_entry_map = entries;
574 LOGD("Linking new m_entry_map ArchiveFileEntries (%d) with ArchiveFile object",
575 m_entry_map->size());
576 for(auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) {
578 it->second->setArchiveFileNonProtectPtr(this);
583 UnZipPtr ArchiveFile::createUnZipObject()
587 LOGE("File is not opened");
588 throw UnknownException("File is not opened");
592 LOGE("m_file is null");
593 throw UnknownException("File is null");
596 Filesystem::NodePtr node = m_file->getNode();
598 LOGE("Node is null");
599 throw UnknownException("Node is null");
602 UnZipPtr unzip = UnZip::open(node->getPath()->getFullPath());
606 ZipPtr ArchiveFile::createZipObject()
610 LOGE("File is not opened");
611 throw UnknownException("File is not opened");
615 LOGE("m_file is null");
616 throw UnknownException("File is null");
619 Filesystem::NodePtr node = m_file->getNode();
621 LOGE("Node is null");
622 throw UnknownException("Node is null");
625 ZipPtr zip = Zip::open(node->getPath()->getFullPath());
630 bool ArchiveFile::isAllowedOperation(const std::string& method_name)
633 PermissionMap::iterator it = s_permission_map.find(method_name);
634 if (it != s_permission_map.end()) {
635 return it->second.permission[m_file_mode];
640 FileMode ArchiveFile::getFileMode() const
646 void ArchiveFile::setFileMode(FileMode file_mode)
649 m_file_mode = file_mode;
652 void ArchiveFile::throwInvalidStateErrorIfArchiveFileIsClosed() const
655 LOGE("ArchiveFile closed - operation not permitted");
656 throw InvalidStateException(
657 "ArchiveFile closed - operation not permitted");
661 void ArchiveFile::setCreatedAsNewEmptyArchive(bool new_and_empty)
663 m_created_as_new_empty_archive = new_and_empty;
666 bool ArchiveFile::isCreatedAsNewEmptyArchive() const
668 return m_created_as_new_empty_archive;
671 void ArchiveFile::updateListOfEntries()
673 // For explanation please see:
674 // ArchiveFile.h m_created_as_new_empty_archive description
676 if(m_file->getNode()->getSize() == 0) {
677 LOGD("Zip file: %s is empty",
678 m_file->getNode()->getPath()->getFullPath().c_str());
680 if(m_created_as_new_empty_archive) {
681 LOGD("OK this is empty archive = nothing to do yet");
685 LOGW("m_created_as_new_empty_archive is false");
686 LOGE("Throwing InvalidStateException: File is not valid ZIP archive");
687 throw InvalidStateException("File is not valid ZIP archive");
691 UnZipPtr unzip = createUnZipObject();
692 unsigned long decompressedSize = 0;
693 ArchiveFileEntryPtrMapPtr emap = unzip->listEntries(&decompressedSize);
695 setDecompressedSize(decompressedSize);
698 bool ArchiveFile::isEntryWithNameInArchive(const std::string& name_in_zip,
699 bool* out_is_directory,
700 std::string* out_matching_name)
703 LOGW("m_entry_map is NULL");
707 const bool name_in_zip_is_dir = isDirectoryPath(name_in_zip);
708 bool set_is_directory = false;
709 bool set_name_exists = false;
712 auto it = m_entry_map->find(name_in_zip);
713 if(it != m_entry_map->end()) {
714 set_is_directory = name_in_zip_is_dir;
715 set_name_exists = true;
718 if(name_in_zip_is_dir) {
719 //If name_in_zip is pointing at directory try file
720 it = m_entry_map->find(removeTrailingDirectorySlashFromPath(name_in_zip));
721 set_is_directory = false;
723 //If name_in_zip is pointing at file try directory
724 it = m_entry_map->find(name_in_zip + "/");
725 set_is_directory = true;
728 if(it != m_entry_map->end()) {
729 set_name_exists = true;
733 if(!set_name_exists) {
737 if(out_is_directory) {
738 *out_is_directory = set_is_directory;
740 if(out_matching_name) {
741 *out_matching_name = it->first;