[Archive] Add C++ code from old implementation.
authorKrzysztof Lachacz <k.lachacz@samsung.com>
Tue, 16 Dec 2014 20:25:02 +0000 (21:25 +0100)
committerKrzysztof Lachacz <k.lachacz@samsung.com>
Sat, 20 Dec 2014 13:38:42 +0000 (14:38 +0100)
[Veryfication] Code compile without errors,
Archive object is available through tizen object.

Change-Id: Id8efcdf429b2eda46dab1f2a2bd06a38984e826c
Signed-off-by: Andrzej Jacak <a.jacak@partner.samsung.com>
Signed-off-by: Krzysztof Lachacz <k.lachacz@samsung.com>
25 files changed:
packaging/webapi-plugins.spec
src/archive/archive.gyp
src/archive/archive_callback_data.cc [new file with mode: 0755]
src/archive/archive_callback_data.h [new file with mode: 0644]
src/archive/archive_file.cc [new file with mode: 0644]
src/archive/archive_file.h [new file with mode: 0755]
src/archive/archive_file_entry.cc [new file with mode: 0644]
src/archive/archive_file_entry.h [new file with mode: 0755]
src/archive/archive_instance.cc
src/archive/archive_manager.cc [new file with mode: 0644]
src/archive/archive_manager.h [new file with mode: 0755]
src/archive/archive_utils.cc [new file with mode: 0644]
src/archive/archive_utils.h [new file with mode: 0644]
src/archive/defs.h [new file with mode: 0755]
src/archive/filesystem_file.cc [new file with mode: 0644]
src/archive/filesystem_file.h [new file with mode: 0755]
src/archive/un_zip.cc [new file with mode: 0644]
src/archive/un_zip.h [new file with mode: 0644]
src/archive/un_zip_extract_request.cc [new file with mode: 0644]
src/archive/un_zip_extract_request.h [new file with mode: 0644]
src/archive/zip.cc [new file with mode: 0644]
src/archive/zip.h [new file with mode: 0644]
src/archive/zip_add_request.cc [new file with mode: 0644]
src/archive/zip_add_request.h [new file with mode: 0644]
src/common/platform_exception.h

index 862fa16..a1176c3 100644 (file)
@@ -153,6 +153,7 @@ BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(libudev)
 BuildRequires: pkgconfig(message-port)
 BuildRequires: pkgconfig(minizip)
+BuildRequires: pkgconfig(zlib)
 BuildRequires: pkgconfig(msg-service)
 BuildRequires: pkgconfig(pkgmgr)
 BuildRequires: pkgconfig(pkgmgr-info)
index a59b89b..999e79d 100644 (file)
@@ -8,19 +8,41 @@
       'type': 'loadable_module',
       'sources': [
         'archive_api.js',
+        #'archive_callback_data.cc',
+        'archive_callback_data.h',
         'archive_extension.cc',
         'archive_extension.h',
+        'archive_file.cc',
+        'archive_file.h',
+        'archive_file_entry.cc',
+        'archive_file_entry.h',
         'archive_instance.cc',
-        'archive_instance.h'
+        'archive_instance.h',
+        'archive_manager.cc',
+        'archive_manager.h',
+        'archive_utils.cc',
+        'archive_utils.h',
+        'filesystem_file.cc'
+        'filesystem_file.h',
+        'defs.h',
+        'un_zip.cc',
+        'un_zip.h',
+        'un_zip_extract_request.cc',
+        'un_zip_extract_request.h',
+        'zip_add_request.cc',
+        'zip_add_request.h',
+        'zip.cc',
+        'zip.h'
       ],
       'includes': [
-        '../common/pkg-config.gypi',
+        '../common/pkg-config.gypi'
       ],
       'conditions': [
         ['tizen == 1', {
           'variables': {
             'packages': [
-              'minizip'
+              'minizip',
+              'zlib'
             ]
           },
         }],
diff --git a/src/archive/archive_callback_data.cc b/src/archive/archive_callback_data.cc
new file mode 100755 (executable)
index 0000000..075ab6d
--- /dev/null
@@ -0,0 +1,791 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+/**
+ * @file        ArchiveCallbackData.cpp
+ */
+
+#include "archive_callback_data.h"
+
+#include "common/logger.h"
+
+//#include <FilesystemExternalUtils.h>
+#include "archive_file.h"
+#include "archive_utils.h"
+#include "un_zip.h"
+#include "zip.h"
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+//namespace {
+//const char* CALLBACK_SUCCESS = "success";
+//const char* CALLBACK_ERROR = "error";
+//const char* CALLBACK_PROGRESS = "progress";
+//} //anonymous namespace
+//
+////----------------------------------------------------------------------------------------
+////OperationCallbackData
+////----------------------------------------------------------------------------------------
+//
+//OperationCallbackData::OperationCallbackData(ArchiveCallbackType callback_type) :
+//    m_callback_type(callback_type),
+//    m_op_id(0),
+//    m_is_error(false),
+//    m_is_canceled(false)
+//{
+//    LOGD("Entered");
+//}
+//
+//OperationCallbackData::~OperationCallbackData()
+//{
+//    LOGD("Entered");
+//    if(m_op_id > 0){
+//        ArchiveManager::getInstance().eraseElementFromArchiveFileMap(m_op_id);
+//    }
+//}
+//
+//void OperationCallbackData::setError(const std::string &err_name,
+//        const std::string &err_message)
+//{
+//    LOGD("Entered");
+//    //store only first error
+//    if (!m_is_error) {
+//        m_err_name = err_name;
+//        m_err_message = err_message;
+//        m_is_error = true;
+//    }
+//}
+//
+//bool OperationCallbackData::isError() const
+//{
+//    LOGD("Entered");
+//    return m_is_error;
+//}
+//
+//bool OperationCallbackData::isCanceled() const
+//{
+//    return m_is_canceled;
+//}
+//
+//void OperationCallbackData::setOperationId(long op_id)
+//{
+//    LOGD("Entered");
+//    m_op_id = op_id;
+//}
+//
+//long OperationCallbackData::getOperationId() const
+//{
+//    LOGD("Entered");
+//    return m_op_id;
+//}
+//
+//void OperationCallbackData::setIsCanceled(bool canceled)
+//{
+//    m_is_canceled = canceled;
+//}
+//
+//const std::string& OperationCallbackData::getErrorName() const
+//{
+//    LOGD("Entered");
+//    return m_err_name;
+//}
+//
+//const std::string& OperationCallbackData::getErrorMessage() const
+//{
+//    LOGD("Entered");
+//    return m_err_message;
+//}
+//
+//ArchiveCallbackType OperationCallbackData::getCallbackType() const
+//{
+//    LOGD("Entered");
+//    return m_callback_type;
+//}
+//
+//ArchiveFilePtr OperationCallbackData::getArchiveFile() const
+//{
+//    return m_caller_instance;
+//}
+//
+//void OperationCallbackData::setArchiveFile(ArchiveFilePtr caller)
+//{
+//    m_caller_instance = caller;
+//}
+//
+//
+////void OperationCallbackData::setSuccessCallback(JSValueRef on_success)
+////{
+////    LOGD("Entered");
+////    auto ctx = getContext();
+////    if(on_success && JSValueIsObject(ctx, on_success)) {
+////        JSObjectRef success = JSValueToObject(ctx, on_success, NULL);
+////        this->setCallback(CALLBACK_SUCCESS, success);
+////    }
+////}
+//
+////void OperationCallbackData::setErrorCallback(JSValueRef on_error)
+////{
+////    LOGD("Entered");
+////    auto ctx = getContext();
+////    if(on_error && JSValueIsObject(ctx, on_error)) {
+////        JSObjectRef error = JSValueToObject(ctx, on_error, NULL);
+////        this->setCallback(CALLBACK_ERROR, error);
+////    }
+////}
+//
+//void OperationCallbackData::callSuccessCallback()
+//{
+//    LOGD("Entered");
+//    LOGW("STUB Not calling success callback");
+//    //this->invokeCallback(CALLBACK_SUCCESS, 0, NULL);
+//}
+//
+////void OperationCallbackData::callSuccessCallback(JSValueRef success)
+////{
+////    LOGD("Entered");
+////    this->invokeCallback(CALLBACK_SUCCESS, success);
+////}
+//
+////void OperationCallbackData::callErrorCallback(JSValueRef err)
+////{
+////    LOGD("Entered");
+////    this->invokeCallback(CALLBACK_ERROR, err);
+////}
+//
+////----------------------------------------------------------------------------------------
+////OpenCallbackData
+////----------------------------------------------------------------------------------------
+//
+//OpenCallbackData::OpenCallbackData(ArchiveCallbackType callback_type):
+//    OperationCallbackData(callback_type)
+//{
+//    LOGD("Entered");
+//}
+//
+//OpenCallbackData::~OpenCallbackData()
+//{
+//    LOGD("Entered");
+//}
+//
+//void OpenCallbackData::executeOperation(ArchiveFilePtr archive_file_ptr)
+//{
+//    LOGE("Entered");
+//
+//    Filesystem::FilePtr file = archive_file_ptr->getFile();
+//    if (!file) {
+//        LOGE("File is null");
+//        throw UnknownException("File is null");
+//    }
+//    Filesystem::NodePtr node = file->getNode();
+//    if(!node) {
+//        LOGE("Node is null");
+//        throw UnknownException("Node is null");
+//    }
+//    const FileMode fm = archive_file_ptr->m_file_mode;
+//    if (0 == node->getSize()) {
+//        if(FileMode::READ_WRITE == fm ||
+//                FileMode::WRITE == fm ||
+//                FileMode::ADD == fm) {
+//            LOGD("Empty file obtained for writing/appending");
+//
+//            // Do not create empty archive with minizip library - it will not be loaded
+//            // by unzip.
+//            //
+//            // For explanation please see:
+//            //    ArchiveFile.h m_created_as_new_empty_archive description
+//            //
+//            archive_file_ptr->setCreatedAsNewEmptyArchive(true);
+//            archive_file_ptr->setEntryMap(ArchiveFileEntryPtrMapPtr(
+//                    new ArchiveFileEntryPtrMap()));
+//            archive_file_ptr->setIsOpen(true);
+//        }
+//        else {
+//            LOGE("The file is empty throwing: InvalidValuesException - Invalid ZIP archive");
+//            throw InvalidValuesException("Invalid ZIP archive");
+//        }
+//    }
+//    else {
+//        archive_file_ptr->setIsOpen(true);
+//        archive_file_ptr->updateListOfEntries();
+//    }
+//
+//    guint id = g_idle_add(ArchiveFile::openTaskCompleteCB, this);
+//    if (!id) {
+//        LOGE("g_idle_add fails");
+//        throw UnknownException("g_idle_add fails");
+//    }
+//}
+//
+////----------------------------------------------------------------------------------------
+////GetEntriesCallbackData
+////----------------------------------------------------------------------------------------
+//
+//GetEntriesCallbackData::GetEntriesCallbackData(ArchiveCallbackType callback_type):
+//    OperationCallbackData(callback_type)
+//{
+//    LOGD("Entered");
+//}
+//GetEntriesCallbackData::~GetEntriesCallbackData()
+//{
+//    LOGD("Entered");
+//}
+//
+//ArchiveFileEntryPtrMapPtr GetEntriesCallbackData::getEntries() const
+//{
+//    return m_entries;
+//}
+//
+//void GetEntriesCallbackData::setEntries(ArchiveFileEntryPtrMapPtr entries)
+//{
+//    m_entries = entries;
+//}
+//
+//void GetEntriesCallbackData::executeOperation(ArchiveFilePtr archive_file_ptr)
+//{
+//    LOGD("Entered");
+//
+//    setEntries(archive_file_ptr->getEntryMap());
+//
+//    guint id = g_idle_add(ArchiveFile::getEntriesTaskCompleteCB, this);
+//    if (!id) {
+//        LOGE("g_idle_add fails");
+//        throw UnknownException("g_idle_add fails");
+//    }
+//}
+//
+////----------------------------------------------------------------------------------------
+////GetEntryByNameCallbackData
+////----------------------------------------------------------------------------------------
+//
+//GetEntryByNameCallbackData::GetEntryByNameCallbackData(ArchiveCallbackType callback_type):
+//    OperationCallbackData(callback_type)
+//{
+//    LOGD("Entered");
+//}
+//
+//GetEntryByNameCallbackData::~GetEntryByNameCallbackData()
+//{
+//    LOGD("Entered");
+//}
+//
+//const std::string& GetEntryByNameCallbackData::getName() const
+//{
+//    LOGD("Entered");
+//    return m_name;
+//}
+//
+//void GetEntryByNameCallbackData::setName(const std::string& name)
+//{
+//    LOGD("Entered");
+//    m_name = name;
+//}
+//
+//ArchiveFileEntryPtr GetEntryByNameCallbackData::getFileEntry() const
+//{
+//    return m_file_entry;
+//}
+//
+//void GetEntryByNameCallbackData::setFileEntry(ArchiveFileEntryPtr entry)
+//{
+//    m_file_entry = entry;
+//}
+//
+//void GetEntryByNameCallbackData::executeOperation(ArchiveFilePtr archive_file_ptr)
+//{
+//    LOGD("Entered");
+//
+//    ArchiveFileEntryPtrMapPtr entries = archive_file_ptr->getEntryMap();
+//    auto it = entries->find(getName());
+//
+//    //Not found but if our name does not contain '/'
+//    //try looking for directory with such name
+//    //
+//    if (it == entries->end() && !isDirectoryPath(getName())) {
+//        const std::string try_directory = getName() + "/";
+//        LOGD("GetEntryByName Trying directory: [%s]", try_directory.c_str());
+//        it = entries->find(try_directory);
+//    }
+//
+//    if (it == entries->end()) {
+//        LOGE("GetEntryByName Entry with name: [%s] not found", getName().c_str());
+//        LOGE("Throwing NotFoundException - Entry not found");
+//        throw NotFoundException("Entry not found");
+//    }
+//
+//    setFileEntry(it->second);
+//
+//    guint id = g_idle_add(ArchiveFile::getEntryByNameTaskCompleteCB, this);
+//    if (!id) {
+//        LOGE("g_idle_add fails");
+//        throw UnknownException("g_idle_add fails");
+//    }
+//}
+//
+////----------------------------------------------------------------------------------------
+////BaseProgressCallback
+////----------------------------------------------------------------------------------------
+//
+//BaseProgressCallback::BaseProgressCallback(ArchiveCallbackType callback_type):
+//    OperationCallbackData(callback_type),
+//    m_overwrite(false)
+//{
+//    LOGD("Entered");
+//}
+//
+//BaseProgressCallback::~BaseProgressCallback()
+//{
+//    LOGD("Entered");
+//}
+//
+//bool BaseProgressCallback::getOverwrite() const
+//{
+//    LOGD("Entered");
+//    return m_overwrite;
+//}
+//
+//void BaseProgressCallback::setOverwrite(bool overwrite)
+//{
+//    LOGD("Entered");
+//    m_overwrite = overwrite;
+//}
+//
+//struct ProgressHolder
+//{
+//    ProgressHolder() :
+//            callback(NULL)
+//    {
+//    };
+//
+//    double overall_progress;
+//    ArchiveFileEntryPtr currently_processed_entry;
+//    BaseProgressCallback* callback;
+//};
+//
+//void BaseProgressCallback::callSuccessCallbackOnMainThread()
+//{
+//    guint id = g_idle_add(BaseProgressCallback::callSuccessCallbackCB,
+//            static_cast<void*>(this));
+//    if (!id) {
+//        LOGE("g_idle_add fails - success callback will not be called");
+//    }
+//}
+//
+//gboolean BaseProgressCallback::callSuccessCallbackCB(void* data)
+//{
+//    BaseProgressCallback* callback = static_cast<BaseProgressCallback*>(data);
+//    if (!callback) {
+//        LOGE("callback pointer is NULL");
+//        return false;
+//    }
+//
+//    std::unique_ptr<BaseProgressCallback> cb_ptr(callback);
+//
+//    LOGW("STUB Not checking if context is still alive");
+//    //JSContextRef context = callback->getContext();
+//    //if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
+//    //    LOGE("context closed - unable to call success callback");
+//    //    return false;
+//    //}
+//
+//    try {
+//        callback->callSuccessCallback();
+//    }
+//    catch (const PlatformException &err) {
+//        LOGE("%s (%s)", err.name().c_str(), err.message().c_str());
+//    }
+//    catch (...) {
+//        LOGE("Unknown error occurs");
+//    }
+//
+//    callback->setArchiveFile(ArchiveFilePtr());
+//    ArchiveManager::getInstance().eraseElementFromArchiveFileMap(callback->m_op_id);
+//
+//    return false;
+//}
+//
+////void BaseProgressCallback::setProgressCallback(JSValueRef on_progress)
+////{
+////    LOGD("Entered");
+////    auto ctx = getContext();
+////    if(on_progress && JSValueIsObject(ctx, on_progress)) {
+////        JSObjectRef progress = JSValueToObject(ctx, on_progress, NULL);
+////        this->setCallback(CALLBACK_PROGRESS, progress);
+////    }
+////}
+//
+//void BaseProgressCallback::callProgressCallback(long operationId,
+//        double value,
+//        const std::string& filename)
+//{
+//    LOGD("Entered");
+//    LOGW("STUB calling progress callback");
+//
+//    //auto ctx = getContext();
+//    //const int SIZE = 3;
+//    //JSValueRef parameters[SIZE] = {
+//    //        JSUtil::toJSValueRef(ctx, operationId),
+//    //        JSUtil::toJSValueRef(ctx, value),
+//    //        JSUtil::toJSValueRef(ctx, filename)
+//    //    };
+//    //
+//    //this->invokeCallback(CALLBACK_PROGRESS, SIZE, parameters);
+//}
+//
+//void BaseProgressCallback::callProgressCallbackOnMainThread(const double progress,
+//        ArchiveFileEntryPtr current_entry)
+//{
+//    ProgressHolder* ph = new(std::nothrow) ProgressHolder();
+//
+//    if(ph) {
+//        ph->overall_progress = progress;
+//        ph->currently_processed_entry = current_entry;
+//        ph->callback = this;
+//
+//        guint id = g_idle_add(BaseProgressCallback::callProgressCallbackCB,
+//                static_cast<void*>(ph));
+//        if (!id) {
+//            LOGE("g_idle_add fails");
+//            delete ph;
+//            ph = NULL;
+//        }
+//    } else {
+//        LOGE("Couldn't allocate ProgressHolder");
+//    }
+//}
+//
+//gboolean BaseProgressCallback::callProgressCallbackCB(void* data)
+//{
+//    ProgressHolder* ph = static_cast<ProgressHolder*>(data);
+//    if (!ph) {
+//        LOGE("ph is null");
+//        return false;
+//    }
+//
+//    std::unique_ptr<ProgressHolder> ph_ptr(ph);
+//    if (!ph->callback) {
+//        LOGE("ph->callback is null");
+//        return false;
+//    }
+//
+//    LOGW("STUB Not checking if context is still alive");
+//    //JSContextRef context = ph->callback->getContext();
+//    //if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
+//    //    LOGE("context was closed");
+//    //    return false;
+//    //}
+//
+//    try {
+//        //Error callback is being handled by ArchiveFile queue - see
+//        //ArchiveFile::taskManagerThread function
+//        //
+//        ph->callback->callProgressCallback(
+//                ph->callback->m_op_id,
+//                ph->overall_progress,
+//                ph->currently_processed_entry->getName());
+//    }
+//    catch (const PlatformException &err) {
+//        LOGE("%s (%s)", err.name().c_str(), err.message().c_str());
+//    }
+//    catch (...) {
+//        LOGE("Unknown error occurs");
+//    }
+//
+//    return false;
+//}
+//
+////----------------------------------------------------------------------------------------
+////AddProgressCallback
+////----------------------------------------------------------------------------------------
+//
+//AddProgressCallback::AddProgressCallback(ArchiveCallbackType callback_type):
+//    BaseProgressCallback(callback_type)
+//{
+//    LOGD("Entered");
+//}
+//
+//AddProgressCallback::~AddProgressCallback()
+//{
+//    LOGD("Entered");
+//}
+//
+//ArchiveFileEntryPtr AddProgressCallback::getFileEntry() const
+//{
+//    LOGD("Entered");
+//    return m_file_entry;
+//}
+//
+//void AddProgressCallback::setFileEntry(ArchiveFileEntryPtr file_entry)
+//{
+//    LOGD("Entered");
+//    m_file_entry = file_entry;
+//}
+//
+//void AddProgressCallback::setBasePath(const std::string& path)
+//{
+//    LOGD("Entered");
+//    m_base_path = path;
+//    m_base_virt_path = Filesystem::External::toVirtualPath(m_base_path);
+//    std::string::size_type pos = m_base_virt_path.find(Filesystem::Path::getSeparator());
+//    if (pos != std::string::npos)
+//    {
+//        m_base_virt_path = m_base_virt_path.substr(pos + 1);
+//    }
+//    else
+//    {
+//        m_base_virt_path = "";
+//    }
+//}
+//
+//const std::string& AddProgressCallback::getBasePath()
+//{
+//    LOGD("Entered");
+//    return m_base_path;
+//}
+//
+//const std::string& AddProgressCallback::getBaseVirtualPath()
+//{
+//    LOGD("Entered");
+//    return m_base_virt_path;
+//}
+//
+//void AddProgressCallback::executeOperation(ArchiveFilePtr archive_file_ptr)
+//{
+//    LOGD("Entered");
+//
+//    if(!m_file_entry) {
+//        LOGE("ArchiveFileEntry is not set in callback");
+//        throw UnknownException("Could not add file to archive");
+//    }
+//
+//    if(!archive_file_ptr) {
+//        LOGE("archive_file_ptr is NULL");
+//        throw UnknownException("Could not extract archive file entry");
+//    }
+//
+//    AddProgressCallback* callback = this;
+//
+//    ZipPtr zip = archive_file_ptr->createZipObject();
+//    zip->addFile(callback);
+//    // Zip is no more needed but it locks file opening while
+//    // it is needed to read entries from file
+//    zip->close();
+//
+//    //We have just finished adding file to archive so now
+//    //this archive file should be valid .zip archive and
+//    //we can remove CreatedAsNewEmptyArchive flag
+//    archive_file_ptr->setCreatedAsNewEmptyArchive(false);
+//
+//    LOGD("Update decompressed size and entry list");
+//    // update informations about decompressed size and entry list
+//    // TODO FIXME need to resolve problem with access to file by
+//    // more than one thread
+//    try{
+//        archive_file_ptr->updateListOfEntries();
+//    } catch(...){
+//        LOGD("Unknown error during updating entries list inside archive");
+//    }
+//}
+//
+////----------------------------------------------------------------------------------------
+////ExtractAllProgressCallback
+////----------------------------------------------------------------------------------------
+//
+//ExtractAllProgressCallback::ExtractAllProgressCallback(ArchiveCallbackType callback_type):
+//    BaseProgressCallback(callback_type),
+//    m_files_to_extract(0),
+//    m_files_extracted(0),
+//    m_current_file_size(0),
+//    m_current_file_extracted_bytes(0),
+//    m_progress_overall(0),
+//    m_overall_decompressed(0)
+//{
+//    LOGD("Entered");
+//}
+//
+//ExtractAllProgressCallback::~ExtractAllProgressCallback()
+//{
+//    LOGD("Entered");
+//}
+//
+//Filesystem::FilePtr ExtractAllProgressCallback::getDirectory() const
+//{
+//    return m_directory;
+//}
+//
+//void ExtractAllProgressCallback::setDirectory(Filesystem::FilePtr directory)
+//{
+//    m_directory = directory;
+//}
+//
+//void ExtractAllProgressCallback::startedExtractingFile(unsigned long current_file_size)
+//{
+//    m_current_file_size = current_file_size;
+//    m_current_file_extracted_bytes = 0;
+//}
+//
+//void ExtractAllProgressCallback::extractedPartOfFile(unsigned long bytes_decompressed)
+//{
+//    m_current_file_extracted_bytes += bytes_decompressed;
+//    updateOverallProgress(bytes_decompressed);
+//}
+//
+//void ExtractAllProgressCallback::finishedExtractingFile()
+//{
+//    m_current_file_size = 0;
+//    m_current_file_extracted_bytes = 0;
+//    ++m_files_extracted;
+//    updateOverallProgress(0);
+//}
+//
+//void ExtractAllProgressCallback::updateOverallProgress(unsigned long bytes_decompressed)
+//{
+//    m_overall_decompressed += bytes_decompressed;
+//    m_progress_overall =
+//            static_cast<double>(m_overall_decompressed + m_files_extracted) /
+//            static_cast<double>(m_expected_decompressed_size + m_files_to_extract);
+//
+//    LOGD("%s of %s - %f%% (%d/%d files)",
+//            bytesToReadableString(m_overall_decompressed).c_str(),
+//            bytesToReadableString(m_expected_decompressed_size).c_str(),
+//            m_progress_overall * 100.0,
+//            m_files_extracted, m_files_to_extract);
+//}
+//
+//double ExtractAllProgressCallback::getCurrentFileProgress() const
+//{
+//    if(m_current_file_size > 0) {
+//        return static_cast<double>(m_current_file_extracted_bytes) /
+//                static_cast<double>(m_current_file_size);
+//    }
+//    else {
+//        return 1.0;
+//    }
+//}
+//
+//double ExtractAllProgressCallback::getOverallProgress() const
+//{
+//    return m_progress_overall;
+//}
+//
+//void ExtractAllProgressCallback::executeOperation(ArchiveFilePtr archive_file_ptr)
+//{
+//    LOGD("Entered");
+//    archive_file_ptr->extractAllTask(this);
+//}
+//
+//void ExtractAllProgressCallback::setExpectedDecompressedSize(unsigned long exp_dec_size)
+//{
+//    m_expected_decompressed_size = exp_dec_size;
+//}
+//
+//unsigned long ExtractAllProgressCallback::getExpectedDecompressedSize() const
+//{
+//    return m_expected_decompressed_size;
+//}
+//
+//void ExtractAllProgressCallback::setNumberOfFilesToExtract(unsigned long files_count)
+//{
+//    m_files_to_extract = files_count;
+//}
+//
+//unsigned long ExtractAllProgressCallback::getNumberOfFilesToExtract() const
+//{
+//    return m_files_to_extract;
+//}
+//
+////----------------------------------------------------------------------------------------
+////OperationCanceledException
+////----------------------------------------------------------------------------------------
+//
+//const char* OPERATION_CANCELED_EXCEPTION = "OperationCanceledException";
+//
+//OperationCanceledException::OperationCanceledException(const char* message)
+//{
+//    LOGD("Entered");
+//}
+//
+////----------------------------------------------------------------------------------------
+////ExtractEntryProgressCallback
+////----------------------------------------------------------------------------------------
+//
+//ExtractEntryProgressCallback::ExtractEntryProgressCallback():
+//        ExtractAllProgressCallback(),
+//        m_strip_name(false)
+//{
+//    LOGD("Entered");
+//    m_callback_type = EXTRACT_ENTRY_PROGRESS_CALLBACK;
+//}
+//
+//ExtractEntryProgressCallback::~ExtractEntryProgressCallback()
+//{
+//    LOGD("Entered");
+//}
+//
+//ArchiveFileEntryPtr ExtractEntryProgressCallback::getArchiveFileEntry()
+//{
+//    return m_archive_file_entry;
+//}
+//
+//void ExtractEntryProgressCallback::setArchiveFileEntry(ArchiveFileEntryPtr afentry)
+//{
+//    m_archive_file_entry = afentry;
+//}
+//
+//void ExtractEntryProgressCallback::setStripName(bool strip_name)
+//{
+//    m_strip_name = strip_name;
+//}
+//
+//bool ExtractEntryProgressCallback::getStripName() const
+//{
+//    return m_strip_name;
+//}
+//
+//void ExtractEntryProgressCallback::setStripBasePath(
+//        const std::string& strip_base_path)
+//{
+//    m_strip_base_path = strip_base_path;
+//}
+//
+//const std::string& ExtractEntryProgressCallback::getStripBasePath() const
+//{
+//    return m_strip_base_path;
+//}
+//
+//void ExtractEntryProgressCallback::executeOperation(ArchiveFilePtr archive_file_ptr)
+//{
+//    LOGD("Entered");
+//
+//    if(!m_archive_file_entry) {
+//        LOGE("ArchiveFileEntry is not set in callback");
+//        throw UnknownException("Could not extract archive file entry");
+//    }
+//
+//    if(!archive_file_ptr) {
+//        LOGE("archive_file_ptr is NULL");
+//        throw UnknownException("Could not extract archive file entry");
+//    }
+//
+//    UnZipPtr unzip = archive_file_ptr->createUnZipObject();
+//    unzip->extractTo(this);
+//}
+
+} //namespace Archive
+} //namespace DeviceAPI
diff --git a/src/archive/archive_callback_data.h b/src/archive/archive_callback_data.h
new file mode 100644 (file)
index 0000000..4dd9fec
--- /dev/null
@@ -0,0 +1,270 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+/**
+ * @file        ArchiveCallbackData.h
+ */
+
+#ifndef __ARCHIVE_CALLBACK_DATA_H__
+#define __ARCHIVE_CALLBACK_DATA_H__
+
+#include <string>
+#include <memory>
+#include <glib.h>
+
+#include "filesystem_file.h"
+
+#include "archive_file_entry.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+class ArchiveFileEntry;
+typedef std::shared_ptr<ArchiveFileEntry> ArchiveFileEntryPtr;
+
+enum ArchiveCallbackType {
+    OPERATION_CALLBACK_DATA = 0,
+    OPEN_CALLBACK_DATA = 1,
+    GET_ENTRIES_CALLBACK_DATA = 2,
+    GET_ENTRY_BY_NAME_CALLBACK_DATA = 3,
+    BASE_PROGRESS_CALLBACK = 4,
+    EXTRACT_ALL_PROGRESS_CALLBACK = 5,
+    EXTRACT_ENTRY_PROGRESS_CALLBACK = 6,
+    ADD_PROGRESS_CALLBACK = 7
+};
+
+class ArchiveFile;
+typedef std::shared_ptr<ArchiveFile> ArchiveFilePtr;
+
+class OperationCallbackData
+{
+public:
+    OperationCallbackData(ArchiveCallbackType callback_type = OPERATION_CALLBACK_DATA);
+    virtual ~OperationCallbackData();
+
+    //void setSuccessCallback(JSValueRef on_success);
+    //void setErrorCallback(JSValueRef on_error);
+
+    void callSuccessCallback();
+    //void callSuccessCallback(JSValueRef err);
+    //void callErrorCallback(JSValueRef err);
+
+    void setError(const std::string &err_name,
+            const std::string &err_message);
+    bool isError() const;
+    const std::string& getErrorName() const;
+    const std::string& getErrorMessage() const;
+
+    void setOperationId(long op_id);
+    long getOperationId() const;
+
+    ArchiveCallbackType getCallbackType() const;
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+
+    bool isCanceled() const;
+    void setIsCanceled(bool is_cancel);
+
+    ArchiveFilePtr getArchiveFile() const;
+    void setArchiveFile(ArchiveFilePtr caller);
+
+protected:
+    ArchiveCallbackType m_callback_type;
+    long m_op_id;
+
+private:
+    bool m_is_error;
+    bool m_is_canceled;
+    std::string m_err_name;
+    std::string m_err_message;
+
+    ArchiveFilePtr m_caller_instance;
+};
+
+class OpenCallbackData : public OperationCallbackData
+{
+public:
+    OpenCallbackData(ArchiveCallbackType callback_type = OPEN_CALLBACK_DATA);
+    virtual ~OpenCallbackData();
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+};
+
+class GetEntriesCallbackData : public OperationCallbackData
+{
+public:
+    GetEntriesCallbackData(ArchiveCallbackType callback_type = GET_ENTRIES_CALLBACK_DATA);
+    virtual ~GetEntriesCallbackData();
+
+    ArchiveFileEntryPtrMapPtr getEntries() const;
+    void setEntries(ArchiveFileEntryPtrMapPtr entries);
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+
+private:
+    ArchiveFileEntryPtrMapPtr m_entries;
+};
+
+class GetEntryByNameCallbackData : public OperationCallbackData
+{
+public:
+    GetEntryByNameCallbackData(ArchiveCallbackType callback_type = GET_ENTRY_BY_NAME_CALLBACK_DATA);
+    virtual ~GetEntryByNameCallbackData();
+
+    const std::string& getName() const;
+    void setName(const std::string& name);
+
+    ArchiveFileEntryPtr getFileEntry() const;
+    void setFileEntry(ArchiveFileEntryPtr entry);
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+private:
+    std::string m_name;
+    ArchiveFileEntryPtr m_file_entry;
+
+};
+
+class BaseProgressCallback : public OperationCallbackData
+{
+public:
+    BaseProgressCallback(ArchiveCallbackType callback_type = BASE_PROGRESS_CALLBACK);
+    virtual ~BaseProgressCallback();
+
+    //void setProgressCallback(JSValueRef on_progress);
+
+    bool getOverwrite() const;
+    void setOverwrite(bool overwrite);
+
+    void callProgressCallbackOnMainThread(const double progress,
+            ArchiveFileEntryPtr current_entry);
+    void callSuccessCallbackOnMainThread();
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+
+protected:
+    void callProgressCallback(long operationId,
+            double value,
+            const std::string& filename);
+
+private:
+    static gboolean callProgressCallbackCB(void* data);
+    static gboolean callSuccessCallbackCB(void* data);
+
+    bool m_overwrite;
+};
+
+class AddProgressCallback : public BaseProgressCallback
+{
+public:
+    AddProgressCallback(ArchiveCallbackType callback_type = ADD_PROGRESS_CALLBACK);
+    virtual ~AddProgressCallback();
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+
+    void setBasePath(const std::string& path);
+    const std::string& getBasePath();
+    const std::string& getBaseVirtualPath();
+
+    ArchiveFileEntryPtr getFileEntry() const;
+    void setFileEntry(ArchiveFileEntryPtr file_entry);
+
+private:
+    ArchiveFileEntryPtr m_file_entry;
+    ArchiveFileEntryPtrMapPtr m_entry_map;
+    std::string m_base_path;
+    std::string m_base_virt_path;
+};
+
+class ExtractAllProgressCallback : public BaseProgressCallback
+{
+public:
+    ExtractAllProgressCallback(ArchiveCallbackType callback_type = EXTRACT_ALL_PROGRESS_CALLBACK);
+    virtual ~ExtractAllProgressCallback();
+
+    Filesystem::FilePtr getDirectory() const;
+    void setDirectory(Filesystem::FilePtr directory);
+
+    void startedExtractingFile(unsigned long current_file_size);
+    void extractedPartOfFile(unsigned long bytes_decompressed);
+    void finishedExtractingFile();
+
+    double getCurrentFileProgress() const;
+    double getOverallProgress() const;
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+
+    void setExpectedDecompressedSize(unsigned long exp_dec_size);
+    unsigned long getExpectedDecompressedSize() const;
+
+    void setNumberOfFilesToExtract(unsigned long files_count);
+    unsigned long getNumberOfFilesToExtract() const;
+
+private:
+    void updateOverallProgress(unsigned long bytes_decompressed);
+
+    Filesystem::FilePtr m_directory;
+
+    //
+    // Constant values set before extracting entries:
+    //
+    unsigned long m_files_to_extract;
+    unsigned long m_expected_decompressed_size;
+
+    //
+    // Values updated during extraction
+    //
+
+    unsigned long m_current_file_size;
+    unsigned long m_current_file_extracted_bytes;
+    unsigned long m_files_extracted;
+
+    double m_progress_overall;
+    unsigned long m_overall_decompressed;
+};
+
+class ExtractEntryProgressCallback : public ExtractAllProgressCallback
+{
+public:
+    ExtractEntryProgressCallback();
+    virtual ~ExtractEntryProgressCallback();
+
+    ArchiveFileEntryPtr getArchiveFileEntry();
+    void setArchiveFileEntry(ArchiveFileEntryPtr afentry);
+
+    void setStripName(bool strip_name);
+    bool getStripName() const;
+
+    void setStripBasePath(const std::string& strip_base_path);
+    const std::string& getStripBasePath() const;
+
+    virtual void executeOperation(ArchiveFilePtr archive_file_ptr);
+
+private:
+    ArchiveFileEntryPtr m_archive_file_entry;
+    bool m_strip_name;
+    std::string m_strip_base_path;
+};
+
+class OperationCanceledException {
+public:
+    OperationCanceledException(const char* message = "Operation Canceled");
+};
+
+}
+}
+
+#endif //__ARCHIVE_CALLBACK_DATA_H__
diff --git a/src/archive/archive_file.cc b/src/archive/archive_file.cc
new file mode 100644 (file)
index 0000000..64d2891
--- /dev/null
@@ -0,0 +1,755 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "archive_file.h"
+
+#include "common/logger.h"
+#include "common/platform_exception.h"
+
+#include "archive_manager.h"
+#include "archive_utils.h"
+#include "defs.h"
+#include "un_zip.h"
+#include "zip.h"
+
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+Permission::Permission(bool r, bool w, bool rw, bool a){
+    permission[0] = r;
+    permission[1] = w;
+    permission[2] = rw;
+    permission[3] = a;
+}
+
+PermissionMap ArchiveFile::s_permission_map = {
+        {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_ADD,
+                Permission(NOT_ALLOWED, ALLOWED, ALLOWED, ALLOWED)},
+        {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_EXTRACT_ALL,
+                Permission(ALLOWED, NOT_ALLOWED, ALLOWED, NOT_ALLOWED)},
+        {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_GET_ENTRIES,
+                Permission(ALLOWED, NOT_ALLOWED, ALLOWED, NOT_ALLOWED)},
+        {ARCHIVE_FUNCTION_API_ARCHIVE_FILE_GET_ENTRY_BY_NAME,
+                Permission(ALLOWED, NOT_ALLOWED, ALLOWED, NOT_ALLOWED)}
+};
+
+ArchiveFile::ArchiveFile() :
+        enable_shared_from_this<ArchiveFile>(),
+        m_decompressed_size(0),
+        m_is_open(false),
+        m_overwrite(false),
+        m_created_as_new_empty_archive(false)
+{
+    LOGD("Entered");
+}
+
+ArchiveFile::ArchiveFile(FileMode file_mode) :
+        enable_shared_from_this<ArchiveFile>(),
+        m_decompressed_size(0),
+        m_is_open(false),
+        m_overwrite(false)
+{
+    m_file_mode = file_mode;
+}
+
+ArchiveFile::~ArchiveFile()
+{
+    LOGD("Entered");
+
+    if(m_entry_map) {
+        LOGD("Unlinking old m_entry_map: %d ArchiveFileEntries", m_entry_map->size());
+        for(auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) {
+            if(it->second) {
+                it->second->setArchiveFileNonProtectPtr(NULL);
+            }
+        }
+    }
+}
+
+gboolean ArchiveFile::openTaskCompleteCB(void *data)
+{
+    LOGD("Entered");
+    LOGW("STUB Not calling success/error callback");
+
+    auto callback = static_cast<OperationCallbackData*>(data);
+    if (!callback) {
+        LOGE("callback is null");
+        return false;
+    }
+
+//     JSContextRef context = callback->getContext();
+//     if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
+//         LOGE("context was closed");
+//         delete callback;
+//         callback = NULL;
+//         return false;
+//     }
+//     try {
+//         if (callback->isError()) {
+//             JSObjectRef errobj = JSWebAPIErrorFactory::makeErrorObject(context,
+//                     callback->getErrorName(),
+//                     callback->getErrorMessage());
+//             callback->callErrorCallback(errobj);
+//         }
+//         else {
+//             JSObjectRef result = JSArchiveFile::makeJSObject(context,
+//                     callback->getArchiveFile());
+//             callback->callSuccessCallback(result);
+//         }
+//     }
+//     catch (const PlatformException &err) {
+//         LOGE("%s (%s)", err.name().c_str(), err.message().c_str());
+//     }
+//     catch (...) {
+//         LOGE("Unknown error occurs");
+//     }
+
+    delete callback;
+    callback = NULL;
+
+    return false;
+}
+
+gboolean ArchiveFile::callErrorCallback(void* data)
+{
+    LOGD("Entered");
+    LOGW("STUB Not calling success/error callback");
+
+    auto callback = static_cast<OperationCallbackData*>(data);
+    if (!callback) {
+        LOGE("callback is null");
+        return false;
+    }
+
+//     JSContextRef context = callback->getContext();
+//     if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
+//         LOGE("context was closed");
+//         delete callback;
+//         callback = NULL;
+//         return false;
+//     }
+// 
+//     try {
+//         if (callback->isError()) {
+//             JSObjectRef errobj = JSWebAPIErrorFactory::makeErrorObject(context,
+//                     callback->getErrorName(),
+//                     callback->getErrorMessage());
+//             callback->callErrorCallback(errobj);
+//         }
+//         else {
+//             LOGW("The success callback should be not be called in this case");
+//         }
+//     }
+//     catch (const PlatformException &err) {
+//         LOGE("%s (%s)", err.name().c_str(), err.message().c_str());
+//     }
+//     catch (...) {
+//         LOGE("Unknown error occurs");
+//     }
+
+    delete callback;
+    callback = NULL;
+
+    return false;
+}
+
+void* ArchiveFile::taskManagerThread(void *data)
+{
+    LOGD("Entered");
+    ArchiveFileHolder* archive_file_holder = static_cast<ArchiveFileHolder*>(data);
+    if (!archive_file_holder) {
+        LOGE("archive_file_holder is null");
+        return NULL;
+    }
+
+    if (!archive_file_holder->ptr){
+        LOGE("archive_file is null");
+        delete archive_file_holder;
+        archive_file_holder = NULL;
+        return NULL;
+    }
+
+    while(true){
+        OperationCallbackData* callback = NULL;
+        bool call_error_callback = false;
+        try{
+            {
+                std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
+                if(archive_file_holder->ptr->m_task_queue.empty()){
+                    break;
+                }
+                callback = archive_file_holder->ptr->m_task_queue.back().second;
+            }
+            if(callback && !callback->isCanceled()){
+                callback->executeOperation(archive_file_holder->ptr);
+            }
+            {
+                std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
+                archive_file_holder->ptr->m_task_queue.pop_back();
+            }
+        } catch (const OperationCanceledException &err) {
+            {
+                std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
+                archive_file_holder->ptr->m_task_queue.pop_back();
+            }
+            delete callback;
+            callback = NULL;
+        } catch (const PlatformException &err) {
+            LOGE("taskManagerThread fails, %s: %s", err.name().c_str(),
+                    err.message().c_str());
+            callback->setError(err.name().c_str(), err.message().c_str());
+            call_error_callback = true;
+        } catch (...) {
+            LOGE("taskManagerThread fails");
+            callback->setError("UnknownError", "UnknownError");
+            call_error_callback = true;
+        }
+        if(call_error_callback) {
+            {
+                std::lock_guard<std::mutex> lock(archive_file_holder->ptr->m_mutex);
+                archive_file_holder->ptr->m_task_queue.pop_back();
+            }
+            if (!g_idle_add(callErrorCallback, static_cast<void*>(callback))) {
+                LOGE("g_idle_add fails");
+                delete callback;
+                callback = NULL;
+            }
+        }
+    }
+
+    delete archive_file_holder;
+    archive_file_holder = NULL;
+
+    return NULL;
+}
+
+long ArchiveFile::addOperation(OperationCallbackData* callback)
+{
+    LOGD("Entered callback type:%d", callback->getCallbackType());
+
+    const long operation_id =
+            ArchiveManager::getInstance().getNextOperationId(shared_from_this());
+    callback->setOperationId(operation_id);
+    callback->setArchiveFile(shared_from_this());
+    std::size_t size = 0;
+    {
+        std::lock_guard<std::mutex> lock(m_mutex);
+        m_task_queue.push_front(CallbackPair(operation_id, callback));
+        size = m_task_queue.size();
+    }
+    if(1 == size) {
+        pthread_t thread;
+        ArchiveFileHolder* holder = new(std::nothrow) ArchiveFileHolder();
+        if(!holder) {
+            LOGE("Memory allocation error");
+            throw UnknownException("Memory allocation error");
+        }
+        holder->ptr = shared_from_this();
+        if (pthread_create(&thread, NULL, taskManagerThread,
+                static_cast<void*>(holder))) {
+            LOGE("Thread creation failed");
+            delete holder;
+            holder = NULL;
+            throw UnknownException("Thread creation failed");
+        }
+
+        if (pthread_detach(thread)) {
+            LOGE("Thread detachment failed");
+        }
+    }
+    return operation_id;
+}
+
+void ArchiveFile::extractAllTask(ExtractAllProgressCallback* callback)
+{
+    Filesystem::FilePtr directory = callback->getDirectory();
+
+    if(!directory) {
+        LOGE("Directory is null");
+        throw UnknownException("Directory is null");
+    } else {
+        if(!directory->getNode()){
+            LOGE("Node in directory is null");
+            throw UnknownException("Node is null");
+        }
+    }
+
+    if(!m_file) {
+        LOGE("File is null");
+        throw UnknownException("File is null");
+    } else {
+        if(!m_file->getNode()){
+            LOGE("Node in file is null");
+            throw UnknownException("Node in file is null");
+        }
+    }
+
+    // For explanation please see:
+    //    ArchiveFile.h m_created_as_new_empty_archive description
+    //
+    if(m_file->getSize() == 0) {
+        LOGD("Zip file: %s is empty",
+                m_file->getFullPath().c_str());
+
+        if(m_created_as_new_empty_archive) {
+            //We do not call progress callback since we do not have any ArchiveFileEntry
+            callback->callSuccessCallbackOnMainThread();
+            callback = NULL;
+            return;
+        }
+        else {
+            LOGW("m_created_as_new_empty_archive is false");
+            LOGE("Throwing InvalidStateException: File is not valid ZIP archive");
+            throw InvalidStateException("File is not valid ZIP archive");
+        }
+    }
+
+    UnZipPtr unzip = createUnZipObject();
+    unzip->extractAllFilesTo(directory->getFullPath(), callback);
+}
+
+long ArchiveFile::getEntries(GetEntriesCallbackData* callback)
+{
+    LOGD("Entered");
+    if(!callback) {
+        LOGE("callback is NULL");
+        throw UnknownException("Could not get list of files in archive");
+    }
+
+    throwInvalidStateErrorIfArchiveFileIsClosed();
+
+    return addOperation(callback);
+}
+
+gboolean ArchiveFile::getEntriesTaskCompleteCB(void *data)
+{
+    LOGD("Entered");
+    LOGW("STUB Not calling success/error callback");
+
+    auto callback = static_cast<GetEntriesCallbackData*>(data);
+    if (!callback) {
+        LOGE("callback is null");
+        return false;
+    }
+
+//     JSContextRef context = callback->getContext();
+//     if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
+//         LOGE("context was closed");
+//         delete callback;
+//         callback = NULL;
+//         return false;
+//     }
+//
+//     try {
+//         ArchiveFileEntryPtrMapPtr entries = callback->getEntries();
+//         unsigned int size = entries->size();
+// 
+//         JSObjectRef objArray[size];
+//         int i = 0;
+//         for(auto it = entries->begin(); it != entries->end(); it++) {
+//             objArray[i] = JSArchiveFileEntry::makeJSObject(context, it->second);
+//             i++;
+//         }
+// 
+//         JSValueRef exception = NULL;
+//         JSObjectRef jsResult = JSObjectMakeArray(context, size,
+//                 size > 0 ? objArray : NULL, &exception);
+//         if (exception != NULL) {
+//             throw Common::UnknownException(context, exception);
+//         }
+// 
+//         callback->callSuccessCallback(jsResult);
+//     }
+//     catch (const PlatformException &err) {
+//         LOGE("%s (%s)", err.name().c_str(), err.message().c_str());
+//     }
+//     catch (...) {
+//         LOGE("Unknown error occurs");
+//     }
+
+    delete callback;
+    callback = NULL;
+
+    return false;
+}
+
+long ArchiveFile::extractAll(ExtractAllProgressCallback *callback)
+{
+    LOGD("Entered");
+    if(!callback) {
+        LOGE("callback is NULL");
+        throw UnknownException("Could not extract all files from archive");
+    }
+
+    throwInvalidStateErrorIfArchiveFileIsClosed();
+
+    return addOperation(callback);
+}
+
+long ArchiveFile::extractEntryTo(ExtractEntryProgressCallback* callback)
+{
+    LOGD("Entered");
+    if(!callback) {
+        LOGE("callback is NULL");
+        throw UnknownException("Could not extract archive file entry");
+    }
+
+    // FIXME according to documentation:
+    // if archive was closed, any further operation attempt will make InvalidStateError
+    // but method extract() from ArchiveFileEntryObject is not permitted to throw above exception
+
+    // uncomment in case when this method have permission to throwing InvalidStateError
+    // throwInvalidStateErrorIfArchiveFileisClosed();
+    if(!m_is_open) {
+        LOGE("Archive is not opened");
+        throw UnknownException("Archive is not opened");
+    }
+
+    return addOperation(callback);
+}
+
+
+long ArchiveFile::add(AddProgressCallback *callback)
+{
+    LOGD("Entered");
+    if(!callback) {
+        LOGE("callback is NULL");
+        throw UnknownException("Could not add file to archive");
+    }
+    if(FileMode::READ == m_file_mode) {
+        LOGE("Trying to add file when READ access mode selected");
+        throw InvalidAccessException("Add not allowed for \"r\" access mode");
+    }
+
+    throwInvalidStateErrorIfArchiveFileIsClosed();
+
+    return addOperation(callback);
+}
+
+void ArchiveFile::close()
+{
+    LOGD("Entered");
+
+    if(!m_is_open){
+        LOGD("Archive already closed");
+    }
+    m_is_open = false;
+
+    return;
+}
+
+long ArchiveFile::getEntryByName(GetEntryByNameCallbackData* callback)
+{
+    LOGD("Entered");
+    if(!callback) {
+        LOGE("callback is NULL");
+        throw UnknownException("Could not get archive file entries by name");
+    }
+
+    throwInvalidStateErrorIfArchiveFileIsClosed();
+
+    return addOperation(callback);
+}
+
+gboolean ArchiveFile::getEntryByNameTaskCompleteCB(void *data)
+{
+    LOGD("Entered");
+    LOGW("STUB Not calling success/error callback");
+
+    auto callback = static_cast<GetEntryByNameCallbackData*>(data);
+    if (!callback) {
+        LOGE("callback is null");
+        return false;
+    }
+
+//     JSContextRef context = callback->getContext();
+//     if (!GlobalContextManager::getInstance()->isAliveGlobalContext(context)) {
+//         LOGE("context was closed");
+//         delete callback;
+//         callback = NULL;
+//         return false;
+//     }
+//     try {
+//         if (callback->isError()) {
+//             JSObjectRef errobj = JSWebAPIErrorFactory::makeErrorObject(context,
+//                     callback->getErrorName(),
+//                     callback->getErrorMessage());
+//             callback->callErrorCallback(errobj);
+//         }
+//         else {
+//             JSObjectRef entry = JSArchiveFileEntry::makeJSObject(context,
+//                     callback->getFileEntry());
+//             callback->callSuccessCallback(entry);
+//         }
+//     }
+//     catch (const PlatformException &err) {
+//         LOGE("%s (%s)", err.name().c_str(), err.message().c_str());
+//     }
+//     catch (...) {
+//         LOGE("Unknown error occurs");
+//     }
+
+    delete callback;
+    callback = NULL;
+
+    return false;
+}
+
+Filesystem::FilePtr ArchiveFile::getFile() const
+{
+    LOGD("Entered");
+    return m_file;
+}
+
+void ArchiveFile::setFile(Filesystem::FilePtr file)
+{
+    LOGD("Entered");
+    m_file = file;
+}
+
+bool ArchiveFile::isOverwrite() const
+{
+    return m_overwrite;
+}
+
+void ArchiveFile::setOverwrite(bool overwrite)
+{
+    LOGD("Entered");
+    m_overwrite = overwrite;
+}
+
+unsigned long ArchiveFile::getDecompressedSize() const
+{
+    LOGD("Entered");
+    return m_decompressed_size;
+}
+
+void ArchiveFile::setDecompressedSize(unsigned long decompressed_size)
+{
+    LOGD("Entered");
+    m_decompressed_size = decompressed_size;
+}
+
+bool ArchiveFile::isOpen() const
+{
+    LOGD("Entered");
+    return m_is_open;
+}
+
+void ArchiveFile::setIsOpen(bool is_open)
+{
+    LOGD("Entered");
+    m_is_open = is_open;
+}
+
+ArchiveFileEntryPtrMapPtr ArchiveFile::getEntryMap() const
+{
+    return m_entry_map;
+}
+
+void ArchiveFile::setEntryMap(ArchiveFileEntryPtrMapPtr entries)
+{
+    LOGD("Entered");
+
+    if(m_entry_map) {
+        LOGD("Unlinking old m_entry_map: %d ArchiveFileEntries", m_entry_map->size());
+        for(auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) {
+            if(it->second) {
+                it->second->setArchiveFileNonProtectPtr(NULL);
+            }
+        }
+    }
+
+    m_entry_map = entries;
+
+    LOGD("Linking new m_entry_map ArchiveFileEntries (%d) with ArchiveFile object",
+            m_entry_map->size());
+    for(auto it = m_entry_map->begin(); it != m_entry_map->end(); ++it) {
+        if(it->second) {
+            it->second->setArchiveFileNonProtectPtr(this);
+        }
+    }
+}
+
+UnZipPtr ArchiveFile::createUnZipObject()
+{
+    LOGD("Entered");
+    if(!m_is_open) {
+        LOGE("File is not opened");
+        throw UnknownException("File is not opened");
+    }
+
+    if (!m_file) {
+        LOGE("m_file is null");
+        throw UnknownException("File is null");
+    }
+
+//     Filesystem::NodePtr node = m_file->getNode();
+//     if(!node) {
+//         LOGE("Node is null");
+//         throw UnknownException("Node is null");
+//     }
+
+    UnZipPtr unzip = UnZip::open(m_file->getFullPath());
+    return unzip;
+}
+
+ZipPtr ArchiveFile::createZipObject()
+{
+    LOGD("Entered");
+    if(!m_is_open) {
+        LOGE("File is not opened");
+        throw UnknownException("File is not opened");
+    }
+
+    if (!m_file) {
+        LOGE("m_file is null");
+        throw UnknownException("File is null");
+    }
+
+//     Filesystem::NodePtr node = m_file->getNode();
+//     if(!node) {
+//         LOGE("Node is null");
+//         throw UnknownException("Node is null");
+//     }
+
+    ZipPtr zip = Zip::open(m_file->getFullPath());
+    return zip;
+
+}
+
+bool ArchiveFile::isAllowedOperation(const std::string& method_name)
+{
+    LOGD("Entered");
+    PermissionMap::iterator it = s_permission_map.find(method_name);
+    if (it != s_permission_map.end()) {
+        return it->second.permission[m_file_mode];
+    }
+    return false;
+}
+
+FileMode ArchiveFile::getFileMode() const
+{
+   LOGD("Entered");
+   return m_file_mode;
+}
+
+void ArchiveFile::setFileMode(FileMode file_mode)
+{
+    LOGD("Entered");
+    m_file_mode = file_mode;
+}
+
+void ArchiveFile::throwInvalidStateErrorIfArchiveFileIsClosed() const
+{
+    if(!m_is_open){
+        LOGE("ArchiveFile closed - operation not permitted");
+        throw InvalidStateException(
+            "ArchiveFile closed - operation not permitted");
+    }
+}
+
+void ArchiveFile::setCreatedAsNewEmptyArchive(bool new_and_empty)
+{
+    m_created_as_new_empty_archive = new_and_empty;
+}
+
+bool ArchiveFile::isCreatedAsNewEmptyArchive() const
+{
+    return m_created_as_new_empty_archive;
+}
+
+void ArchiveFile::updateListOfEntries()
+{
+    // For explanation please see:
+    //    ArchiveFile.h m_created_as_new_empty_archive description
+    //
+    if(m_file->getSize() == 0) {
+        LOGD("Zip file: %s is empty",
+                m_file->getFullPath().c_str());
+
+        if(m_created_as_new_empty_archive) {
+            LOGD("OK this is empty archive = nothing to do yet");
+            return;
+        }
+        else {
+            LOGW("m_created_as_new_empty_archive is false");
+            LOGE("Throwing InvalidStateException: File is not valid ZIP archive");
+            throw InvalidStateException("File is not valid ZIP archive");
+        }
+    }
+
+    UnZipPtr unzip = createUnZipObject();
+    unsigned long decompressedSize = 0;
+    ArchiveFileEntryPtrMapPtr emap = unzip->listEntries(&decompressedSize);
+    setEntryMap(emap);
+    setDecompressedSize(decompressedSize);
+}
+
+bool ArchiveFile::isEntryWithNameInArchive(const std::string& name_in_zip,
+        bool* out_is_directory,
+        std::string* out_matching_name)
+{
+    if(!m_entry_map) {
+        LOGW("m_entry_map is NULL");
+        return false;
+    }
+
+    const bool name_in_zip_is_dir = isDirectoryPath(name_in_zip);
+    bool set_is_directory = false;
+    bool set_name_exists = false;
+
+    //Try exact name:
+    auto it = m_entry_map->find(name_in_zip);
+    if(it != m_entry_map->end()) {
+        set_is_directory = name_in_zip_is_dir;
+        set_name_exists = true;
+    }
+    else {
+        if(name_in_zip_is_dir) {
+            //If name_in_zip is pointing at directory try file
+            it = m_entry_map->find(removeTrailingDirectorySlashFromPath(name_in_zip));
+            set_is_directory = false;
+        } else {
+            //If name_in_zip is pointing at file try directory
+            it = m_entry_map->find(name_in_zip + "/");
+            set_is_directory = true;
+        }
+
+        if(it != m_entry_map->end()) {
+            set_name_exists = true;
+        }
+    }
+
+    if(!set_name_exists) {
+        return false;
+    }
+
+    if(out_is_directory) {
+        *out_is_directory = set_is_directory;
+    }
+    if(out_matching_name) {
+        *out_matching_name = it->first;
+    }
+
+    return true;
+}
+
+} // Archive
+} // DeviceAPI
diff --git a/src/archive/archive_file.h b/src/archive/archive_file.h
new file mode 100755 (executable)
index 0000000..00d6dd5
--- /dev/null
@@ -0,0 +1,189 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef _TIZEN_ARCHIVE_ARCHIVE_FILE_H_
+#define _TIZEN_ARCHIVE_ARCHIVE_FILE_H_
+
+#include <deque>
+#include <memory>
+#include <mutex>
+#include <string>
+
+#include "archive_callback_data.h"
+#include "archive_manager.h"
+#include "filesystem_file.h"
+#include "un_zip.h"
+#include "zip.h"
+
+
+namespace DeviceAPI {
+namespace Archive {
+
+class ArchiveFile;
+class ArchiveManager;
+class OperationCallbackData;
+class OpenCallbackData;
+class GetEntriesCallbackData;
+class GetEntryByNameCallbackData;
+class BaseProgressCallback;
+class AddProgressCallback;
+class ExtractAllProgressCallback;
+class UnZipExtractRequest;
+class ExtractEntryProgressCallback;
+class Zip;
+class ZipAddRequest;
+
+enum FileMode {
+    READ = 0,
+    WRITE,
+    READ_WRITE,
+    ADD
+};
+
+enum IsAllowed {
+    NOT_ALLOWED = false,
+    ALLOWED = true
+};
+
+struct Permission {
+    Permission(bool r, bool w, bool rw, bool a);
+    bool permission[4];
+};
+
+typedef std::shared_ptr<ArchiveFile> ArchiveFilePtr;
+typedef std::pair<long, OperationCallbackData*> CallbackPair;
+typedef std::vector<ArchiveFileEntryPtr> ArchiveFileEntryPtrVector;
+typedef std::map<std::string, Permission> PermissionMap;
+typedef std::pair<std::string, Permission> PermissionPair;
+
+struct ArchiveFileHolder{
+    ArchiveFilePtr ptr;
+};
+
+class ArchiveFile : public std::enable_shared_from_this<ArchiveFile> {
+public:
+    ArchiveFile();
+    ArchiveFile(FileMode file_mode);
+    virtual ~ArchiveFile();
+
+    long getEntries(GetEntriesCallbackData* callback);
+    long getEntryByName(GetEntryByNameCallbackData* callback);
+    long extractAll(ExtractAllProgressCallback *callback);
+    long add(AddProgressCallback *callback);
+    void close();
+
+    Filesystem::FilePtr getFile() const;
+    void setFile(Filesystem::FilePtr file);
+    bool isOverwrite() const;
+    void setOverwrite(bool overwrite);
+    unsigned long getDecompressedSize() const;
+    void setDecompressedSize(unsigned long decompressed_size);
+    bool isOpen() const;
+    void setIsOpen(bool is_open);
+
+    ArchiveFileEntryPtrMapPtr getEntryMap() const;
+    void setEntryMap(ArchiveFileEntryPtrMapPtr entries);
+    bool isEntryWithNameInArchive(const std::string& name_in_zip,
+            bool* out_is_directory = NULL,
+            std::string* out_matching_name = NULL);
+
+
+    //Used by ArchiveFileEntry
+    long extractEntryTo(ExtractEntryProgressCallback* callback);
+
+    bool isAllowedOperation(const std::string& method_name);
+    FileMode getFileMode() const;
+    void setFileMode(FileMode file_mode);
+
+    /**
+     *  \brief Throw InvalidStateError in case when ArchiveFile is closed
+     */
+    void throwInvalidStateErrorIfArchiveFileIsClosed() const;
+
+    void setCreatedAsNewEmptyArchive(bool new_and_empty);
+    bool isCreatedAsNewEmptyArchive() const;
+
+
+    void updateListOfEntries();
+private:
+    UnZipPtr createUnZipObject();
+    ZipPtr createZipObject();
+
+    std::deque<CallbackPair> m_task_queue;
+    std::mutex m_mutex;
+
+    Filesystem::FilePtr m_file;
+
+    FileMode m_file_mode;
+    static PermissionMap s_permission_map;
+
+    unsigned long m_decompressed_size;
+    bool m_is_open;
+    /**
+     * If set to true, during decompression archive will had permission to overwriting files.
+     * Warning: If decompressing file have got the same name as existing directory
+     * in place where file should be decompressed, directory will be deleted.
+    */
+    bool m_overwrite;
+    ArchiveFileEntryPtrMapPtr m_entry_map;
+
+
+    /**
+     * If we execute tizen.open(destFile , "w"/"rw"/ "a", ..., {overwrite: true}),
+     * destFile will be empty file with size = 0, which cannot be
+     * opened with minizip library.
+     *
+     * Zip file format restricts that at least one file / folder is compressed,
+     * threfore after creating new empty archive we cannot save it.
+     * Until we execute archive.add destFile is empty file with size 0 bytes.
+     *
+     * Unfortunately if we try to execute archive.getEntries or archive.extractAll
+     * minizip library will fail to open empty archive. To fix this issue we will
+     * use flag "created_as_new_empty_archive" which informs that this is new and
+     * empty archive threfore we should not try to open it.
+     *
+     * In extractAll we will just call success callback - there was nothing to extract
+     * but no error occured. In get entries we just should return empty list of entries.
+     */
+    bool m_created_as_new_empty_archive;
+
+    static gboolean openTaskCompleteCB(void *data);
+    static gboolean getEntriesTaskCompleteCB(void *data);
+    static gboolean getEntryByNameTaskCompleteCB(void *data);
+
+    static void* taskManagerThread(void *data);
+    long addOperation(OperationCallbackData* callback);
+    static gboolean callErrorCallback(void* data);
+
+    void extractAllTask(ExtractAllProgressCallback* callback);
+
+    friend class ExtractAllProgressCallback;
+    friend class UnZipExtractRequest;
+    friend class OpenCallbackData;
+    friend class GetEntriesCallbackData;
+    friend class GetEntryByNameCallbackData;
+    friend class ExtractEntryProgressCallback;
+    friend class ArchiveManager;
+    friend class AddProgressCallback;
+    friend class Zip;
+    friend class ZipAddRequest;
+    friend class BaseProgressCallback;
+};
+
+} // Archive
+} // DeviceAPI
+
+#endif /* _TIZEN_ARCHIVE_FILE_ENTRY_H_ */
diff --git a/src/archive/archive_file_entry.cc b/src/archive/archive_file_entry.cc
new file mode 100644 (file)
index 0000000..1615dde
--- /dev/null
@@ -0,0 +1,172 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "archive_file_entry.h"
+
+#include "common/logger.h"
+#include "common/platform_exception.h"
+
+#include "archive_callback_data.h"
+#include "archive_file.h"
+#include "archive_utils.h"
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+static const unsigned int s_default_compression_level = 5;
+
+ArchiveFileEntry::ArchiveFileEntry(Filesystem::FilePtr file) :
+        std::enable_shared_from_this<ArchiveFileEntry>(),
+        m_file(file),
+        m_archive(NULL),
+        m_striped(false),
+        m_size(0),
+        m_compressed_size(0),
+        m_modified(0),
+        m_compression_level(s_default_compression_level)
+{
+    LOGD("Entered");
+}
+
+ArchiveFileEntry::~ArchiveFileEntry()
+{
+    LOGD("Entered");
+}
+
+unsigned long ArchiveFileEntry::getCompressedSize() const
+{
+    return m_compressed_size;
+}
+
+void ArchiveFileEntry::setCompressedSize(unsigned long compressed_size)
+{
+    m_compressed_size = compressed_size;
+}
+
+Filesystem::FilePtr ArchiveFileEntry::getFile() const
+{
+    return m_file;
+}
+
+void ArchiveFileEntry::setFile(Filesystem::FilePtr file)
+{
+    m_file = file;
+}
+
+unsigned long ArchiveFileEntry::getSize() const
+{
+    return m_size;
+}
+
+void ArchiveFileEntry::setSize(unsigned long size)
+{
+    m_size = size;
+}
+
+const std::string& ArchiveFileEntry::getName() const
+{
+    return m_name;
+}
+
+void ArchiveFileEntry::setName(const std::string& name)
+{
+    m_name = name;
+}
+
+void ArchiveFileEntry::setModified(time_t time)
+{
+    m_modified = time;
+}
+
+time_t ArchiveFileEntry::getModified() const
+{
+    return m_modified;
+}
+
+const std::string& ArchiveFileEntry::getDestination() const
+{
+    return m_destination;
+}
+
+void ArchiveFileEntry::setDestination(const std::string& destination)
+{
+    m_destination = destination;
+}
+
+bool ArchiveFileEntry::getStriped() const
+{
+    return m_striped;
+}
+
+void ArchiveFileEntry::setStriped(bool striped)
+{
+    m_striped = striped;
+}
+
+void ArchiveFileEntry::setCompressionLevel(unsigned int level)
+{
+    m_compression_level = level;
+}
+unsigned int ArchiveFileEntry::getCompressionLevel() const
+{
+    return m_compression_level;
+}
+
+void ArchiveFileEntry::setArchiveFileNonProtectPtr(ArchiveFile* ptr)
+{
+    m_archive = ptr;
+}
+
+ArchiveFile* ArchiveFileEntry::getArchiveFileNonProtectPtr()
+{
+    return m_archive;
+}
+
+long ArchiveFileEntry::extractTo(ExtractEntryProgressCallback* callback)
+{
+    if(!m_archive) {
+        LOGE("m_archive is NULL");
+        throw UnknownException("Could not extract archive file entry");
+    }
+
+    //Callback should be associated with this instance of ArchiveFileEntry
+    callback->setArchiveFileEntry(shared_from_this());
+
+    //
+    // If strip name was set in JS layer we need to generate srip base path
+    //
+    if(callback->getStripName()) {
+
+        //Get base path - left side of last slash
+        std::string base_path_name = getBasePathFromPath(m_name);
+        if(!isDirectoryPath(base_path_name) && !base_path_name.empty()) {
+            base_path_name += "/";
+        }
+
+        LOGD("strip name is: true; archive file entry name is: [%s]; "
+                "stripBasePath will be: [%s]",
+                m_name.c_str(), base_path_name.c_str());
+
+        callback->setStripBasePath(base_path_name);
+    }
+
+    return m_archive->extractEntryTo(callback);
+}
+
+} // Archive
+} // DeviceAPI
diff --git a/src/archive/archive_file_entry.h b/src/archive/archive_file_entry.h
new file mode 100755 (executable)
index 0000000..601691f
--- /dev/null
@@ -0,0 +1,81 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef _TIZEN_ARCHIVE_ARCHIVE_FILE_ENTRY_H_
+#define _TIZEN_ARCHIVE_ARCHIVE_FILE_ENTRY_H_
+
+#include <map>
+#include <memory>
+
+#include "filesystem_file.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+class ArchiveFile;
+
+class ArchiveFileEntry;
+typedef std::shared_ptr<ArchiveFileEntry> ArchiveFileEntryPtr;
+
+class ExtractEntryProgressCallback;
+
+typedef std::map<std::string, ArchiveFileEntryPtr> ArchiveFileEntryPtrMap;
+typedef std::shared_ptr<ArchiveFileEntryPtrMap> ArchiveFileEntryPtrMapPtr;
+
+class ArchiveFileEntry :  public std::enable_shared_from_this<ArchiveFileEntry> {
+public:
+    ArchiveFileEntry(Filesystem::FilePtr file = Filesystem::FilePtr());
+    ~ArchiveFileEntry();
+
+    unsigned long getCompressedSize() const;
+    void setCompressedSize(unsigned long compressedSize);
+    Filesystem::FilePtr getFile() const;
+    void setFile(Filesystem::FilePtr file);
+    unsigned long getSize() const;
+    void setSize(unsigned long size);
+    const std::string& getName() const;
+    void setName(const std::string& name);
+    void setModified(time_t time);
+    time_t getModified() const;
+    const std::string& getDestination() const;
+    void setDestination(const std::string& destination);
+    bool getStriped() const;
+    void setStriped(bool striped);
+    void setCompressionLevel(unsigned int level);
+    unsigned int getCompressionLevel() const;
+
+    void setArchiveFileNonProtectPtr(ArchiveFile* ptr);
+    ArchiveFile* getArchiveFileNonProtectPtr();
+
+    long extractTo(ExtractEntryProgressCallback* callback);
+
+private:
+    Filesystem::FilePtr m_file;
+    ArchiveFile* m_archive;
+    std::string m_name;
+    std::string m_destination;
+    bool m_striped;
+    unsigned long m_size;
+    unsigned long m_compressed_size;
+    time_t m_modified;
+    unsigned int m_compression_level;
+};
+
+} // Archive
+} // DeviceAPI
+
+#endif /* _TIZEN_ARCHIVE_FILE_ENTRY_H_ */
index 0fd75f1..ef46d65 100644 (file)
@@ -4,12 +4,19 @@
 
 #include "archive/archive_instance.h"
 
+
+//#include "archive_manager.h"
+
 #include <functional>
 
 #include "common/picojson.h"
 #include "common/logger.h"
 #include "common/platform_exception.h"
 
+
+//using namespace DeviceAPI;
+//using namespace DeviceAPI::Archive;
+
 namespace extension {
 namespace archive {
 
diff --git a/src/archive/archive_manager.cc b/src/archive/archive_manager.cc
new file mode 100644 (file)
index 0000000..4f17bbb
--- /dev/null
@@ -0,0 +1,90 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "archive_manager.h"
+
+#include <mutex>
+
+#include "common/logger.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+ArchiveManager::ArchiveManager():
+    m_next_unique_id(0)
+{
+    LOGD("Initialize ArchiveManager");
+}
+
+ArchiveManager::~ArchiveManager()
+{
+    LOGD("Deinitialize ArchiveManager");
+}
+
+ArchiveManager& ArchiveManager::getInstance()
+{
+    LOGD("Entered");
+    static ArchiveManager instance;
+    return instance;
+}
+
+void ArchiveManager::abort(long operation_id)
+{
+    LOGD("Entered");
+
+    ArchiveFileMap::iterator it = m_archive_file_map.find(operation_id);
+    if (it != m_archive_file_map.end()) {
+        ArchiveFilePtr archive_file_ptr = it->second;
+        std::lock_guard<std::mutex> lock(archive_file_ptr->m_mutex);
+
+        std::size_t size = archive_file_ptr->m_task_queue.size();
+        for(int i = 0; i < size; ++i){
+            if(operation_id == archive_file_ptr->m_task_queue[i].first){
+                archive_file_ptr->m_task_queue[i].second->setIsCanceled(true);
+                return;
+            }
+        }
+    }
+    LOGD("The Operation Identifier not found");
+}
+
+long ArchiveManager::open(OpenCallbackData* callback)
+{
+    LOGD("Entered");
+
+    ArchiveFilePtr a_ptr = callback->getArchiveFile();
+    return a_ptr->addOperation(callback);
+}
+
+long ArchiveManager::getNextOperationId(ArchiveFilePtr archive_file_ptr)
+{
+    LOGD("Entered");
+    long op_id = ++m_next_unique_id;
+    m_archive_file_map.insert(ArchiveFilePair(op_id, archive_file_ptr));
+    return op_id;
+}
+
+void ArchiveManager::eraseElementFromArchiveFileMap(long operation_id)
+{
+    LOGD("Entered");
+    ArchiveFileMap::iterator it = m_archive_file_map.find(operation_id);
+    if (it != m_archive_file_map.end()) {
+        m_archive_file_map.erase(it);
+    }
+}
+
+} // Archive
+} // DeviceAPI
diff --git a/src/archive/archive_manager.h b/src/archive/archive_manager.h
new file mode 100755 (executable)
index 0000000..204eb34
--- /dev/null
@@ -0,0 +1,58 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef _TIZEN_ARCHIVE_ARCHIVE_MANAGER_H_
+#define _TIZEN_ARCHIVE_ARCHIVE_MANAGER_H_
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "archive_file.h"
+#include "archive_callback_data.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+typedef std::map<long, ArchiveFilePtr> ArchiveFileMap;
+typedef std::pair<long, ArchiveFilePtr> ArchiveFilePair;
+
+class ArchiveManager {
+public:
+    static ArchiveManager& getInstance();
+    ~ArchiveManager();
+
+    void abort(long operation_id);
+    long getNextOperationId(ArchiveFilePtr archive_file_ptr);
+    void eraseElementFromArchiveFileMap(long operation_id);
+    long open(OpenCallbackData* callback);
+
+private:
+    ArchiveManager();
+    ArchiveManager(ArchiveManager const&);
+    void operator=(ArchiveManager const&);
+
+    ArchiveFileMap m_archive_file_map;
+
+    long m_next_unique_id;
+
+};
+
+} // Archive
+} // DeviceAPI
+
+#endif /* _TIZEN_ARCHIVE_ARCHIVE_MANAGER_H_ */
diff --git a/src/archive/archive_utils.cc b/src/archive/archive_utils.cc
new file mode 100644 (file)
index 0000000..0097a8d
--- /dev/null
@@ -0,0 +1,258 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "archive_utils.h"
+
+#include <sstream>
+#include <iomanip>
+
+#include "common/logger.h"
+
+//TODO:
+//#include <FilesystemExternalUtils.h>
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+using namespace DeviceAPI::Filesystem;
+
+std::string bytesToReadableString(const size_t num_bytes)
+{
+    std::stringstream ss;
+    static const size_t one_mb = 1024 * 1024;
+    static const size_t one_kb = 1024;
+    ss << std::setprecision(2) << std::fixed;
+
+    if(num_bytes >= one_mb) {
+        ss << (float)num_bytes / one_mb << " MB";
+    } else if(num_bytes >= one_kb) {
+        ss << (float)num_bytes / one_kb << " KB";
+    } else {
+        ss << num_bytes << " B";
+    }
+
+    return ss.str();
+}
+
+std::string fileModeToString(FileMode fm)
+{
+    switch(fm) {
+        case FileMode::READ:
+            return "r";
+        case FileMode::WRITE:
+            return "w";
+        case FileMode::READ_WRITE:
+            return "rw";
+        case FileMode::ADD:
+            return "a";
+        default:
+            throw UnknownException("Unknown file mode");
+    }
+}
+
+FileMode stringToFileMode(std::string fmString)
+{
+    if (!fmString.compare("r")) {
+        return FileMode::READ;
+    }
+    else if (!fmString.compare("w")) {
+        return FileMode::WRITE;
+    }
+    else if(!fmString.compare("rw")) {
+        return FileMode::READ_WRITE;
+    }
+    else if(!fmString.compare("a")) {
+        return FileMode::ADD;
+    }
+    // In widl it's "TypeMismatchError" so this exception used
+    // instead of InvalidValues
+    throw TypeMismatchException("Invalid FileMode");
+}
+
+// FilePtr fileReferenceToFile(JSContextRef context, JSValueRef fileReference)
+// {
+//     auto g_ctx = GlobalContextManager::getInstance()->getGlobalContext(context);
+// 
+//     FilePtr file_ptr;
+//     try {
+//         file_ptr = JSFile::getPrivateObject(context, fileReference);
+//     } catch (const TypeMismatchException &tme) {
+//         LOGD("Use virtual path.");
+//         std::string virtual_path =
+//             JSUtil::JSValueToString(context, fileReference);
+//         if (!External::isVirtualPath(virtual_path)) {
+//             LOGE("FileReference can be File object or a virtual path");
+//             throw TypeMismatchException(
+//                 "FileReference can be File object or a virtual path");
+//         }
+//         std::string string_path =
+//             External::fromVirtualPath(virtual_path, g_ctx);
+//         LOGD("Path: %s", string_path.c_str());
+// 
+//         PathPtr path = Path::create(string_path);
+//         NodePtr node_ptr = Node::resolve(path);
+//         file_ptr = FilePtr(new File(node_ptr, File::PermissionList()));
+//     }
+// 
+//     return file_ptr;
+// }
+
+void getBasePathAndName(const std::string& filepath,
+        std::string& out_basepath,
+        std::string& out_name)
+{
+    const size_t filepath_len = filepath.length();
+
+    size_t name_end_index = filepath_len;
+    size_t name_start_index = 0;
+
+    for(int i = filepath_len - 1; i >= 0; --i) {
+        const char& cur = filepath[i];
+        if(cur == '/' || cur == '\\') {
+            if( (filepath_len-1) == i ) {
+                name_end_index = i;
+            } else {
+                name_start_index = i+1;
+                out_name = filepath.substr(name_start_index,
+                    name_end_index - name_start_index);
+
+                out_basepath = filepath.substr(0, name_start_index);
+                return;
+            }
+        }
+    }
+
+    // \ / is not found
+    out_basepath = "";
+    out_name = filepath.substr(0, name_end_index);
+}
+
+std::string removeDuplicatedSlashesFromPath(const std::string& path)
+{
+    const size_t path_len = path.length();
+
+    std::string out;
+    out.reserve(path_len);
+
+    bool prev_is_dir = false;
+    for(size_t i = 0; i < path_len; ++i) {
+        const char& cur = path[i];
+        if(cur == '/' || cur == '\\') {
+            if(!prev_is_dir) {
+                out += cur;
+            }
+            prev_is_dir = true;
+        } else {
+            prev_is_dir = false;
+            out += cur;
+        }
+    }
+
+    return out;
+}
+
+bool isDirectoryPath(const std::string& path)
+{
+    if(path.empty()) {
+        return false;
+    }
+
+    const char last_char = path[path.length() - 1];
+    return last_char ==  '\\' || last_char == '/';
+}
+
+std::string removeTrailingDirectorySlashFromPath(const std::string& path)
+{
+    if(!isDirectoryPath(path)) {
+        return path;
+    }
+
+    return path.substr(0, path.length() - 1);
+}
+
+std::string stripBasePathFromPath(const std::string& fullpath)
+{
+    const size_t location = fullpath.find_last_of("/\\");
+    if(std::string::npos == location) {
+        return fullpath;
+    }
+
+    return fullpath.substr(location + 1);
+}
+
+namespace{
+static std::string errErrno = "ERRNO";
+static std::string errEndOfListOfFile = "End list of file";
+static std::string errParamater = "Invalid parameter";
+static std::string errBadFile = "Incorrect file";
+static std::string errInternal = "Internal error";
+static std::string errCRC = "CRC error";
+static std::string errUnknown = "Unknown error";
+
+const std::string& getArchiveErrorMessage(int errorCode)
+{
+    /**
+     * All errors are defined in minizip library in files:
+     * zip.h and unzip.h
+     */
+    switch (errorCode) {
+        // ZIP_ERRNO & UNZ_ERRNO both value Z_ERRNO
+        case ZIP_ERRNO:
+            return errErrno;
+        // UNZ_END_OF_LIST_OF_FILE both value -100
+        case UNZ_END_OF_LIST_OF_FILE:
+            return errEndOfListOfFile;
+        // ZIP_PARAMERROR & UNZ_PARAMERROR both value -102
+        case ZIP_PARAMERROR:
+            return errParamater;
+        // ZIP_BADZIPFILE & UNZ_BADZIPFILE both value -103
+        case ZIP_BADZIPFILE:
+            return errBadFile;
+        // ZIP_INTERNALERROR & UNZ_INTERNALERROR bot value -104
+        case ZIP_INTERNALERROR:
+            return errInternal;
+        // UNZ_CRCERROR -105
+        case UNZ_CRCERROR:
+            return errCRC;
+        default:
+            return errUnknown;
+    }
+}
+}
+
+std::string getBasePathFromPath(const std::string& fullpath)
+{
+    const std::string tmp_path = removeTrailingDirectorySlashFromPath(fullpath);
+    const size_t location = tmp_path.find_last_of("/\\");
+    if(std::string::npos == location) {
+        return std::string();
+    }
+
+    return tmp_path.substr(0, location + 1);
+}
+
+std::string getArchiveLogMessage(const int errorCode, const std::string &hint)
+{
+    std::stringstream ss;
+    ss << "Failed " << hint << " : " << getArchiveErrorMessage(errorCode) << ", " << errorCode;
+    return std::string(ss.str());
+}
+
+} //namespace Archive
+} //namespace DeviceAPI
diff --git a/src/archive/archive_utils.h b/src/archive/archive_utils.h
new file mode 100644 (file)
index 0000000..05eaf48
--- /dev/null
@@ -0,0 +1,125 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef __TIZEN_ARCHIVE_ARCHIVE_UTILS_H__
+#define __TIZEN_ARCHIVE_ARCHIVE_UTILS_H__
+
+#include <string>
+
+#include "common/platform_exception.h"
+
+#include "filesystem_file.h"
+#include "archive_file.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+std::string bytesToReadableString(const size_t num_bytes);
+std::string fileModeToString(FileMode fm);
+FileMode stringToFileMode(std::string fmString);
+
+//extern Filesystem::FilePtr fileReferenceToFile(
+//    JSContextRef context, JSValueRef fileReference);
+
+/**
+ * Gets base path and name from full path string, example cases:
+ * full path             |  base path        | name
+ * ----------------------+-------------------+------------------
+ * ""                    | ""                | ""
+ * "/opt/usr/media/TEST" | "/opt/usr/media/" | "TEST"
+ * "/opt/usr/media/TEST/"| "/opt/usr/media/" | "TEST"
+ * "opt/usr/media/TEST"  | "opt/usr/media/"  | "TEST"
+ * "opt/usr/media/TEST/" | "opt/usr/media/"  | "TEST"
+ * "TEST"                | ""                | "TEST"
+ * "TEST/"               | ""                | "TEST"
+ * "/TEST/"              | "/"               | "TEST"
+ */
+void getBasePathAndName(const std::string& filepath,
+        std::string& out_basepath,
+        std::string& out_name);
+
+/**
+ * If path contains duplicated slashes they will be removed
+ *
+ * Note this function does not fix slash style '/', '\'
+ * (Linux/Windows)
+ *
+ * Example path:              | Result:
+ * ---------------------------+------------------------------
+ * "my/a//b/c/d/e"            | "my/a/b/c/d/e"
+ * "add\ff\\\g"               | "add\f\g"
+ * "first//second\\a\/"       | "first/second\a"  (mixed / with \)
+ */
+std::string removeDuplicatedSlashesFromPath(const std::string& path);
+
+/**
+ * Return true if last character of string is '/' or '\'
+ */
+bool isDirectoryPath(const std::string& path);
+
+/**
+ * If path contains trailing '/' or '\' it will be removed.
+ *
+ * Example path:              | Result:
+ * ---------------------------+------------------------------
+ * "documents/someName/"      | "documents/someName"
+ * "documents/yetAnotherName" | "documents/yetAnotherName"
+ */
+std::string removeTrailingDirectorySlashFromPath(const std::string& path);
+
+/**
+ * Returns FILE name without leading base path:
+ *
+ * Example path:              | Result:
+ * ---------------------------+------------------------------
+ * "documents/ABC/a.txt"      | "a.txt"
+ * "documents/A/X/Y/Z/my.log" | "my.log"
+ *
+ * "documents/A/B/C/"         | "" (fullpath is directory)
+ * "a.txt"                    | "a.txt"
+ * "A/"                       | "" (fullpath is directory)
+ */
+std::string stripBasePathFromPath(const std::string& fullpath);
+
+/**
+ * Returns path without last directory or file part
+ *
+ * Example path:              | Result:               | Extracted ending:
+ * ---------------------------+-----------------------+--------------
+ * "documents/ABC/a.txt"      | "documents/ABC/"      | "a.txt"
+ * "documents/A/X/Y/Z/my.log" | "documents/A/X/Y/Z/"  | "my.log"
+ *
+ * "documents/A/B/C/"         | "documents/A/B/"      | "C/"
+ * "a.txt"                    | ""                    | "a.txt"
+ * "A/"                       | ""                    | "A/"
+ */
+std::string getBasePathFromPath(const std::string& fullpath);
+
+std::string getArchiveLogMessage(const int errorCode, const std::string &hint);
+
+template <class T = common::UnknownException>
+void throwArchiveException(const int errorCode, const std::string &hint)
+{
+    std::string log = getArchiveLogMessage(errorCode, hint);
+    LOGE("%s", log.c_str());
+    throw T(log.c_str());
+}
+
+} //namespace Archive
+} //namespace DeviceAPI
+
+#endif // __TIZEN_ARCHIVE_ARCHIVE_UTILS_H__
diff --git a/src/archive/defs.h b/src/archive/defs.h
new file mode 100755 (executable)
index 0000000..ed9f2d9
--- /dev/null
@@ -0,0 +1,36 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef _ARCHIVE_PLUGIN_DEFS_H_
+#define _ARCHIVE_PLUGIN_DEFS_H_
+
+#define TIZEN_ARCHIVE_ARCHIVE_CLASS                             "archive"
+
+#define ARCHIVE_FUNCTION_API_ARCHIVE_MANAGER_ABORT              "abort"
+#define ARCHIVE_FUNCTION_API_ARCHIVE_MANAGER_OPEN               "open"
+
+#define ARCHIVE_FUNCTION_API_ARCHIVE_FILE_ADD                   "add"
+#define ARCHIVE_FUNCTION_API_ARCHIVE_FILE_EXTRACT_ALL           "extractAll"
+#define ARCHIVE_FUNCTION_API_ARCHIVE_FILE_GET_ENTRIES           "getEntries"
+#define ARCHIVE_FUNCTION_API_ARCHIVE_FILE_GET_ENTRY_BY_NAME     "getEntryByName"
+#define ARCHIVE_FUNCTION_API_ARCHIVE_FILE_CLOSE                 "close"
+
+#define ARCHIVE_FUNCTION_API_ARCHIVE_FILE_ENTRY_EXTRACT         "extract"
+
+
+
+#endif // _ARCHIVE_PLUGIN_DEFS_H_
diff --git a/src/archive/filesystem_file.cc b/src/archive/filesystem_file.cc
new file mode 100644 (file)
index 0000000..3b4269a
--- /dev/null
@@ -0,0 +1,28 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#include "filesystem_file.h"
+
+#include "common/logger.h"
+#include "common/platform_exception.h"
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Filesystem {
+
+} // Filesystem
+} // DeviceAPI
diff --git a/src/archive/filesystem_file.h b/src/archive/filesystem_file.h
new file mode 100755 (executable)
index 0000000..55a3bfd
--- /dev/null
@@ -0,0 +1,118 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+#ifndef _TIZEN_FILESYSTEM_FILE_H_
+#define _TIZEN_FILESYSTEM_FILE_H_
+
+#include <string>
+#include <memory>
+#include <vector>
+
+#include "common/logger.h"
+
+namespace DeviceAPI {
+namespace Filesystem {
+
+enum NodeType
+{
+    NT_DIRECTORY,
+    NT_FILE
+};
+
+class File;
+typedef std::shared_ptr<File> FilePtr;
+typedef std::shared_ptr<File> NodePtr;  //STUB bad trick
+typedef std::vector<NodePtr> NodeList;  //STUB bad trick
+
+class File : public std::enable_shared_from_this<File>
+{
+public:
+    File()
+    {
+    }
+
+    File(const std::string& fname) :
+            m_full_path(fname)
+    {
+    }
+
+    FilePtr getNode()
+    {
+        LOGW("STUB");
+        return shared_from_this();
+    }
+
+    size_t getSize()
+    {
+        LOGW("STUB");
+        return 0;
+    }
+
+    FilePtr getPath()
+    {
+        LOGW("STUB");
+        return shared_from_this();
+    }
+
+    //! \brief Gets type of current node.
+    //! @return Node's type.
+    NodeType getType() const
+    {
+        LOGW("STUB");
+        return NT_FILE;
+    }
+
+    const std::string& getFullPath() const
+    {
+        return m_full_path;
+    }
+
+    std::string m_full_path;
+};
+
+class Path;
+typedef std::shared_ptr<Path> PathPtr;
+
+class Path : public std::enable_shared_from_this<Path>
+{
+public:
+    typedef char SeparatorType;
+    static Path::SeparatorType getSeparator()
+    {
+        LOGW("STUB");
+        return '/';
+    }
+};
+
+class External {
+public:
+    static std::string toVirtualPath(const std::string& path)
+    {
+        LOGW("STUB Not implemented path -> virtual path. Return not changed path");
+        return path;
+    }
+
+    static void removeDirectoryRecursive(const std::string& fullpath)
+    {
+        LOGW("STUB Not implemented. Directory: [%s] will not be removed!", fullpath.c_str());
+    }
+};
+
+
+} // Filesystem
+} // DeviceAPI
+
+#endif /* _TIZEN_FILESYSTEM_FILE_H_ */
diff --git a/src/archive/un_zip.cc b/src/archive/un_zip.cc
new file mode 100644 (file)
index 0000000..3b0ebc9
--- /dev/null
@@ -0,0 +1,426 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "un_zip.h"
+
+#include <cstdio>
+#include <errno.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "common/logger.h"
+#include "common/platform_exception.h"
+#include "filesystem_file.h"
+
+#include "archive_file.h"
+#include "archive_utils.h"
+#include "un_zip_extract_request.h"
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+UnZip::UnZip(const std::string& filename) :
+        m_zipfile_name(filename),
+        m_unzip(NULL),
+        m_default_buffer_size(1024 * 1024)
+{
+    LOGD("Entered");
+
+    m_unzip = unzOpen(filename.c_str());
+    if(!m_unzip) {
+        LOGE("unzOpen returned NULL : It means the file is invalid.");
+        throw InvalidValuesException("Failed to open zip file");
+    }
+    m_is_open = true;
+}
+
+UnZip::~UnZip()
+{
+    close();
+}
+
+void UnZip::close()
+{
+    LOGD("Entered");
+    if(!m_is_open) {
+        LOGD("Unzip already closed - exiting");
+        return;
+    }
+
+    int errclose = unzClose(m_unzip);
+    m_unzip = NULL;
+
+    if (errclose != UNZ_OK) {
+        LOGE("ret: %d",errclose);
+        throwArchiveException(errclose, "unzClose()");
+    }
+    m_is_open = false;
+}
+
+UnZipPtr UnZip::open(const std::string& filename)
+{
+    LOGD("Entered");
+    return UnZipPtr(new UnZip(filename));
+}
+
+ArchiveFileEntryPtrMapPtr UnZip::listEntries(unsigned long *decompressedSize)
+{
+    if(!m_is_open) {
+        LOGE("Failed to get list of entries - UnZip closed");
+        throw UnknownException("Failed to get list of files in zip archive");
+    }
+    unz_global_info gi;
+    int err = unzGetGlobalInfo (m_unzip, &gi);
+    if (UNZ_OK != err) {
+        LOGE("ret: %d",err);
+        throwArchiveException(err, "unzGetGlobalInfo()");
+    }
+
+    char filename_inzip[512];
+    unz_file_info file_info;
+
+    ArchiveFileEntryPtrMapPtr map = ArchiveFileEntryPtrMapPtr(
+            new ArchiveFileEntryPtrMap());
+
+    unsigned long totalDecompressed = 0;
+
+    err = unzGoToFirstFile(m_unzip);
+    if (err != UNZ_OK) {
+        LOGW("%s",getArchiveLogMessage(err, "unzGoToFirstFile()").c_str());
+    }
+
+    for (uLong i = 0; i < gi.number_entry; i++) {
+
+        err = unzGetCurrentFileInfo(m_unzip, &file_info,
+                filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);
+        if (err != UNZ_OK) {
+            LOGE("ret: %d",err);
+            throwArchiveException(err, "unzGetCurrentFileInfo()");
+        }
+
+        LOGD("file: %s | unc size: %d | comp size: %d", filename_inzip,
+                file_info.uncompressed_size, file_info.compressed_size);
+
+        ArchiveFileEntryPtr entry = ArchiveFileEntryPtr(new ArchiveFileEntry());
+        entry->setName(filename_inzip);
+        entry->setSize(file_info.uncompressed_size);
+        entry->setCompressedSize(file_info.compressed_size);
+
+        totalDecompressed += file_info.uncompressed_size;
+
+        tm date;// = file_info.tmu_date;
+        date.tm_sec = file_info.tmu_date.tm_sec;
+        date.tm_min = file_info.tmu_date.tm_min;
+        date.tm_hour = file_info.tmu_date.tm_hour;
+        date.tm_mday = file_info.tmu_date.tm_mday;
+        date.tm_mon = file_info.tmu_date.tm_mon;
+        date.tm_year = file_info.tmu_date.tm_year - 1900;
+        date.tm_wday = 0;
+        date.tm_yday = 0;
+        date.tm_isdst = 0;
+        LOGD("%d, %d, %d, %d, %d, %d", date.tm_hour, date.tm_min, date.tm_sec, date.tm_mday, date.tm_mon, date.tm_year);
+        entry->setModified(mktime(&date));
+
+        map->insert(std::make_pair(filename_inzip, entry));
+
+        if ( (i+1) < gi.number_entry) {
+
+            err = unzGoToNextFile(m_unzip);
+            if (UNZ_OK != err) {
+                LOGE("ret: %d",err);
+                throwArchiveException(err, "unzGoToNextFile()");
+            }
+        }
+    }
+
+    (*decompressedSize) = totalDecompressed;
+
+    return map;
+}
+
+void UnZip::extractAllFilesTo(const std::string& extract_path,
+        ExtractAllProgressCallback* callback)
+{
+    if(!m_is_open) {
+        LOGE("Failed to extract files - UnZip closed");
+        throw UnknownException("Failed to extract zip archive");
+    }
+
+    //
+    // Calculate number of entries to extract and total number of bytes
+    //
+    unz_global_info gi;
+    updateCallbackWithArchiveStatistics(callback, gi);
+
+    //
+    // Begin extracting entries
+    //
+    int err = unzGoToFirstFile(m_unzip);
+    if (err != UNZ_OK) {
+        LOGW("%s",getArchiveLogMessage(err, "unzGoToFirstFile()").c_str());
+    }
+
+    for (uLong i = 0; i < gi.number_entry; i++) {
+
+        if (callback->isCanceled()) {
+            LOGD("Operation cancelled");
+            throw OperationCanceledException();
+        }
+
+        extractCurrentFile(extract_path, std::string(), callback);
+
+        if ((i + 1) < gi.number_entry) {
+            err = unzGoToNextFile(m_unzip);
+            if (UNZ_OK != err) {
+                LOGE("ret: %d",err);
+                throwArchiveException(err, "unzGoToNextFile()");
+            }
+        }
+    }
+
+    callback->callSuccessCallbackOnMainThread();
+    callback = NULL;
+}
+
+struct ExtractDataHolder
+{
+    UnZip* unzip;
+    ExtractEntryProgressCallback* callback;
+    std::string root_output_path;
+};
+
+void UnZip::extractTo(ExtractEntryProgressCallback* callback)
+{
+    if(!m_is_open) {
+        LOGE("Extract archive file entry failed - UnZip closed");
+        throw UnknownException("Extract archive file entry failed");
+    }
+
+    if(!callback) {
+        LOGE("callback is NULL");
+        throw UnknownException("Extract archive file entry failed");
+    }
+
+    if(!callback->getArchiveFileEntry()) {
+        LOGE("callback->getArchiveFileEntry() is NULL");
+        throw UnknownException("Extract archive file entry failed");
+    }
+
+    Filesystem::FilePtr out_dir = callback->getDirectory();
+    if(!out_dir) {
+        LOGE("Output directory is not valid");
+        throw InvalidValuesException("Output directory is not correct");
+    }
+
+//     Filesystem::NodePtr out_node = out_dir->getNode();
+//     if(!out_node) {
+//         LOGE("Output directory is not valid");
+//         throw InvalidValuesException("Output directory is not correct");
+//     }
+//
+//     Filesystem::PathPtr out_path = out_node->getPath();
+//     if(!out_path) {
+//         LOGE("Output directory is not valid");
+//         throw InvalidValuesException("Output directory is not correct");
+//     }
+
+    auto entry_name_in_zip = callback->getArchiveFileEntry()->getName();
+    auto root_output_path = out_dir->getFullPath();
+    LOGD("Extract: [%s] to root output directory: [%s] (stripBasePath: [%s])",
+            entry_name_in_zip.c_str(),
+            root_output_path.c_str(),
+            callback->getStripBasePath().c_str());
+
+
+    //
+    // Calculate number of entries to extract and total number of bytes
+    //
+    unz_global_info gi;
+    updateCallbackWithArchiveStatistics(callback, gi, entry_name_in_zip);
+
+    //
+    // Begin extracting entries
+    //
+
+    ExtractDataHolder h;
+    h.unzip = this;
+    h.callback = callback;
+    h.root_output_path = root_output_path;
+
+    // this loop call internally progress callbacks
+    IterateFilesInZip(gi, entry_name_in_zip, callback, extractItFunction, &h);
+
+    // after finish extracting success callback will be called
+    callback->callSuccessCallbackOnMainThread();
+    callback = NULL;
+}
+
+void UnZip::extractItFunction(const std::string& file_name, unz_file_info& file_info,
+        void* user_data)
+{
+    ExtractDataHolder* h = static_cast<ExtractDataHolder*>(user_data);
+    if(!h) {
+        LOGE("ExtractDataHolder is NULL!");
+        throw UnknownException("Could not list content of zip archive");
+    }
+
+    h->unzip->extractCurrentFile(h->root_output_path,
+            h->callback->getStripBasePath(),
+            h->callback);
+}
+
+unsigned int UnZip::IterateFilesInZip(unz_global_info& gi,
+        const std::string& entry_name_in_zip,
+        OperationCallbackData* callback,
+        UnZip::IterateFunction itfunc,
+        void* user_data)
+{
+    int err = unzGoToFirstFile(m_unzip);
+    if (UNZ_OK != err) {
+        LOGW("%s",getArchiveLogMessage(err, "unzGoToFirstFile()").c_str());
+    }
+
+    unsigned int num_file_or_folder_matched = 0;
+    const bool is_directory = isDirectoryPath(entry_name_in_zip);
+
+    unz_file_info cur_file_info;
+    char tmp_fname[512];
+
+    for (uLong i = 0; i < gi.number_entry; i++) {
+
+        if (callback->isCanceled()) {
+            LOGD("Operation cancelled");
+            throw OperationCanceledException();
+        }
+
+        err = unzGetCurrentFileInfo(m_unzip, &cur_file_info,
+                tmp_fname, sizeof(tmp_fname), NULL, 0, NULL, 0);
+        if (UNZ_OK != err) {
+            LOGE("ret: %d",err);
+            throwArchiveException(err, "unzGetCurrentFileInfo()");
+        }
+
+        const std::string cur_filename_in_zip(tmp_fname);
+        bool match = true;
+
+        if(!entry_name_in_zip.empty()) {
+            if(is_directory) {
+                //If entry_name_in_zip is pointing at directory we need to check each entry in
+                //zip if its path starts with entry_name_in_zip
+                match = (0 == cur_filename_in_zip.find(entry_name_in_zip));
+            } else {
+                //If entry name points to file we only extract entry with matching name
+                match = (cur_filename_in_zip == entry_name_in_zip);
+            }
+        }
+
+        if(match) {
+            itfunc(cur_filename_in_zip, cur_file_info, user_data);
+            ++num_file_or_folder_matched;
+        }
+
+        if ((i + 1) < gi.number_entry) {
+            err = unzGoToNextFile(m_unzip);
+            if (UNZ_OK != err) {
+                LOGE("ret: %d",err);
+                throwArchiveException(err, "unzGoToNextFile()");
+            }
+        }
+    }
+
+    return num_file_or_folder_matched;
+}
+
+void UnZip::extractCurrentFile(const std::string& extract_path,
+        const std::string& base_strip_path,
+        BaseProgressCallback* callback)
+{
+    LOGD("Entered");
+
+    if (callback->isCanceled()) {
+        LOGD("Operation cancelled");
+        throw OperationCanceledException();
+    }
+
+    LOGD("extract_path: [%s] base_strip_path: [%s] ", extract_path.c_str(),
+            base_strip_path.c_str());
+    UnZipExtractRequest::execute(*this, extract_path, base_strip_path, callback);
+}
+
+struct ArchiveStatistics
+{
+    ArchiveStatistics() : uncompressed_size(0),
+            number_of_files(0),
+            number_of_folders(0) {}
+
+    unsigned long uncompressed_size;
+    unsigned long number_of_files;
+    unsigned long number_of_folders;
+};
+
+void generateArchiveStatistics(const std::string& file_name, unz_file_info& file_info,
+         void* user_data)
+{
+    if(user_data) {
+        ArchiveStatistics* astats = static_cast<ArchiveStatistics*>(user_data);
+        astats->uncompressed_size += file_info.uncompressed_size;
+
+        if(isDirectoryPath(file_name)) {
+            astats->number_of_folders += 1;
+        }
+        else {
+            astats->number_of_files += 1;
+        }
+    }
+}
+
+void UnZip::updateCallbackWithArchiveStatistics(ExtractAllProgressCallback* callback,
+        unz_global_info& out_global_info,
+        const std::string optional_filter)
+{
+    int err = unzGetGlobalInfo(m_unzip, &out_global_info);
+    if (UNZ_OK != err) {
+        LOGE("ret: %d",err);
+        throwArchiveException(err, "unzGetGlobalInfo()");
+    }
+
+    ArchiveStatistics astats;
+    const auto num_matched = IterateFilesInZip(out_global_info, optional_filter,
+            callback, generateArchiveStatistics, &astats);
+    if(0 == num_matched) {
+        LOGE("No matching file/directory: [%s] has been found in zip archive",
+                optional_filter.c_str());
+        LOGE("Throwing NotFoundException - Could not extract file from archive");
+        throw NotFoundException("Could not extract file from archive");
+    }
+
+    callback->setExpectedDecompressedSize(astats.uncompressed_size);
+    LOGD("Expected uncompressed size: %s",
+            bytesToReadableString(astats.uncompressed_size).c_str());
+
+    callback->setNumberOfFilesToExtract(astats.number_of_files);
+    LOGD("Number entries to extract: files: %d folders: %d", astats.number_of_files,
+            astats.number_of_folders);
+}
+
+} //namespace Archive
+} //namespace DeviceAPI
diff --git a/src/archive/un_zip.h b/src/archive/un_zip.h
new file mode 100644 (file)
index 0000000..0ccbe24
--- /dev/null
@@ -0,0 +1,99 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef __TIZEN_ARCHIVE_UNZIP_H__
+#define __TIZEN_ARCHIVE_UNZIP_H__
+
+#include <memory>
+#include <string>
+#include <queue>
+
+#include <unzip.h>
+
+#include "archive_callback_data.h"
+#include "archive_file_entry.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+class UnZipExtractRequest;
+
+typedef std::shared_ptr<std::vector<char>> CharVectorPtr;
+
+class UnZip;
+typedef std::shared_ptr<UnZip> UnZipPtr;
+
+class UnZip
+{
+public:
+    static UnZipPtr open(const std::string& filename);
+    ~UnZip();
+
+    ArchiveFileEntryPtrMapPtr listEntries(unsigned long *decompressedSize);
+
+    /**
+     * \brief Extract all files to output directory
+     * \param callback which keep pointer to ArchiveFile.
+     */
+    void extractAllFilesTo(const std::string& extract_path,
+            ExtractAllProgressCallback* callback);
+
+    void extractTo(ExtractEntryProgressCallback* callback);
+
+    void close();
+
+private:
+    UnZip(const std::string& filename);
+
+    /**
+     * \brief Extract current file (iterated with minizip library)
+     * \param callback which keep pointer to ArchiveFile.
+     */
+    void extractCurrentFile(const std::string& extract_path,
+            const std::string& base_strip_path,
+            BaseProgressCallback* callback);
+
+    static void extractItFunction(const std::string& file_name,
+            unz_file_info& file_info,
+            void* user_data);
+
+    typedef void (*IterateFunction) (const std::string& file_name,
+            unz_file_info& file_info,
+            void* user_data);
+
+    unsigned int IterateFilesInZip(unz_global_info& gi,
+            const std::string& entry_name_in_zip,
+            OperationCallbackData* callback,
+            IterateFunction itfunc,
+            void* user_data);
+
+    void updateCallbackWithArchiveStatistics(ExtractAllProgressCallback* callback,
+            unz_global_info& out_global_info,
+            const std::string optional_filter = std::string());
+
+    std::string m_zipfile_name;
+    unzFile m_unzip;
+    size_t m_default_buffer_size;
+    bool m_is_open;
+
+    friend class UnZipExtractRequest;
+};
+
+} //namespace Archive
+} //namespace DeviceAPI
+
+#endif // __TIZEN_ARCHIVE_ZIP_H__
diff --git a/src/archive/un_zip_extract_request.cc b/src/archive/un_zip_extract_request.cc
new file mode 100644 (file)
index 0000000..fdcb1e4
--- /dev/null
@@ -0,0 +1,508 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "un_zip_extract_request.h"
+
+#include <cstdio>
+#include <errno.h>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+#include <utime.h>
+
+#include "common/logger.h"
+#include "common/platform_exception.h"
+#include "filesystem_file.h"
+
+#include "archive_file.h"
+#include "archive_utils.h"
+#include "un_zip.h"
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+FilePathStatus getPathStatus(const std::string& path)
+{
+    if(path.empty()) {
+        return FPS_NOT_EXIST;
+    }
+
+    std::string npath = removeTrailingDirectorySlashFromPath(path);
+
+    struct stat sb;
+    if (stat(npath.c_str(), &sb) == -1) {
+        return FPS_NOT_EXIST;
+    }
+    if(sb.st_mode & S_IFDIR) {
+        return FPS_DIRECTORY;
+    } else {
+        return FPS_FILE;
+    }
+}
+
+void divideToPathAndName(const std::string& filepath, std::string& out_path,
+        std::string& out_name)
+{
+    size_t pos_last_dir = filepath.find_last_of("/\\");
+    if(pos_last_dir == std::string::npos) {
+        out_path = "";
+        out_name = filepath;
+    } else {
+        out_path = filepath.substr(0, pos_last_dir+1);
+        out_name = filepath.substr(pos_last_dir+1);
+    }
+}
+
+void createMissingDirectories(const std::string& path, bool check_first = true)
+{
+    if(check_first) {
+        const FilePathStatus path_status = getPathStatus(path);
+        //LOGD("[%s] status: %d", path.c_str(), path_status);
+        if(FPS_DIRECTORY == path_status) {
+            return;
+        }
+    }
+
+    const size_t extract_path_len = path.length();
+    for(size_t i = 0; i < extract_path_len; ++i) {
+        const char& cur = path[i];
+        if( (('\\' == cur || '/' == cur) && i > 0) ||   //handle left side from current /
+                (extract_path_len-1 == i) ) {           //handle last subdirectory path
+
+            const std::string left_part = path.substr(0,i+1);
+            const FilePathStatus status = getPathStatus(left_part);
+            //LOGD("left_part: [%s] status:%d", left_part.c_str(), status);
+
+            if(FPS_DIRECTORY != status) {
+                //TODO investigate 0775 (mode) - Filesystem assumed that file should have parent mode
+                if(mkdir(left_part.c_str(), 0775) == -1) {
+                    LOGE("Couldn't create new directory: %s errno:%s",
+                            left_part.c_str(), strerror(errno));
+               //TODO check why mkdir return -1 but directory is successfully created
+               //     throw UnknownException(
+               //             "Could not create new directory");
+                }
+            }
+        }
+    }
+}
+
+void changeFileAccessAndModifyDate(const std::string& filepath, tm_unz tmu_date)
+{
+  struct utimbuf ut;
+  struct tm newdate;
+  newdate.tm_sec = tmu_date.tm_sec;
+  newdate.tm_min = tmu_date.tm_min;
+  newdate.tm_hour = tmu_date.tm_hour;
+  newdate.tm_mday = tmu_date.tm_mday;
+  newdate.tm_mon = tmu_date.tm_mon;
+
+  if (tmu_date.tm_year > 1900) {
+      newdate.tm_year = tmu_date.tm_year - 1900;
+  } else {
+      newdate.tm_year = tmu_date.tm_year ;
+  }
+  newdate.tm_isdst = -1;
+
+  ut.actime = ut.modtime = mktime(&newdate);
+  if(utime(filepath.c_str(), &ut) == -1) {
+      LOGE("Couldn't set time for: [%s] errno:%s", filepath.c_str(), strerror(errno));
+  }
+}
+
+void UnZipExtractRequest::execute(UnZip& owner, const std::string& extract_path,
+        const std::string& base_strip_path,
+        BaseProgressCallback* callback)
+{
+    UnZipExtractRequest req(owner, extract_path, base_strip_path, callback);
+    req.run();
+}
+
+UnZipExtractRequest::UnZipExtractRequest(UnZip& owner,
+        const std::string& extract_path,
+        const std::string& base_strip_path,
+        BaseProgressCallback* callback) :
+
+        m_owner(owner),
+        m_extract_path(extract_path),
+        m_base_strip_path(base_strip_path),
+        m_callback(callback),
+
+        m_output_file(NULL),
+        m_buffer(NULL),
+        m_delete_output_file(false),
+        m_close_unz_current_file(false),
+        m_new_dir_status(FPS_NOT_EXIST),
+
+        m_is_directory_entry(false)
+{
+    if(!m_callback){
+        LOGE("Callback is null");
+        throw UnknownException("Problem with callback functionality");
+    }
+}
+
+void UnZipExtractRequest::run()
+{
+    LOGD("Entered");
+
+    getCurrentFileInfo();
+
+    if(m_is_directory_entry) {
+        handleDirectoryEntry();
+    } else {
+        handleFileEntry();
+    }
+}
+
+UnZipExtractRequest::~UnZipExtractRequest()
+{
+    if(m_output_file) {
+        fclose(m_output_file);
+        m_output_file = NULL;
+    }
+
+    if(m_delete_output_file && !m_is_directory_entry) {
+        if(std::remove(m_output_filepath.c_str()) != 0) {
+            LOGE("Couldn't remove partial file! "
+                    "std::remove(\"%s\") failed with errno:%s",
+                    m_output_filepath.c_str(), strerror(errno));
+        }
+    }
+
+    delete [] m_buffer;
+    m_buffer = NULL;
+
+    if(m_close_unz_current_file) {
+        int err = unzCloseCurrentFile (m_owner.m_unzip);
+        if(UNZ_OK != err) {
+            LOGW("%s",getArchiveLogMessage(err, "unzCloseCurrentFile()").c_str());
+        }
+    }
+}
+
+void UnZipExtractRequest::getCurrentFileInfo()
+{
+    LOGD("Entered");
+    int err = unzGetCurrentFileInfo(m_owner.m_unzip, &m_file_info,
+            m_filename_inzip, sizeof(m_filename_inzip), NULL, 0, NULL, 0);
+    if (err != UNZ_OK) {
+        LOGE("ret: %d", err);
+        throwArchiveException(err, "unzGetCurrentFileInfo()");
+    }
+
+    LOGD("Input from ZIP: m_filename_inzip: [%s]", m_filename_inzip);
+    LOGD("m_base_strip_path: [%s]", m_base_strip_path.c_str());
+
+    std::string file_path = m_filename_inzip;
+    if(!m_base_strip_path.empty()) {
+        if(file_path.find(m_base_strip_path) != 0) {
+            LOGW("m_base_strip_path: [%s] is not begin of m_filename_inzip: [%s]!",
+                    m_base_strip_path.c_str(),
+                    m_filename_inzip);
+        }
+        else {
+            file_path = file_path.substr(m_base_strip_path.length());
+            LOGD("Stripped file name: [%s]", file_path.c_str());
+        }
+    }
+    else {
+        LOGD("Not stripped file name: [%s]", file_path.c_str());
+    }
+
+    m_output_filepath = removeDuplicatedSlashesFromPath(m_extract_path + "/" + file_path);
+
+    LOGD("Packed: [%s], uncompressed_size: %d, will extract to: [%s]",
+            m_filename_inzip, m_file_info.uncompressed_size,
+            m_output_filepath.c_str());
+
+    std::string path, name;
+    divideToPathAndName(file_path, path, name);
+    m_new_dir_path = removeDuplicatedSlashesFromPath(m_extract_path + "/" + path);
+    m_new_dir_status = getPathStatus(m_new_dir_path);
+    m_is_directory_entry = name.empty();
+
+    LOGD("New output dir: [%s] status: %d m_is_directory_entry: %d",
+            m_new_dir_path.c_str(), m_new_dir_status, m_is_directory_entry);
+
+    if(FPS_DIRECTORY != m_new_dir_status) {
+
+        if(m_is_directory_entry) {
+
+            std::string base_directories;
+            const size_t len = m_new_dir_path.length();
+
+            for(int i = static_cast<int>(len) - 2; i >= 0; i--) {  //skip last \, /
+                const char& cur = m_new_dir_path[i];
+                if('\\' == cur || '/' == cur) {
+                    base_directories = m_new_dir_path.substr(0, static_cast<size_t>(i));
+                    break;
+                }
+            }
+
+            LOGD("Type: DIRECTORY checking base output directories: [%s]",
+                    base_directories.c_str());
+            createMissingDirectories(base_directories, false);
+        } else {
+            LOGD("Type: FILE checking output dir: [%s]", m_new_dir_path.c_str());
+            createMissingDirectories(m_new_dir_path, false);
+        }
+    }
+}
+
+void UnZipExtractRequest::handleDirectoryEntry()
+{
+    LOGD("Entered");
+    if(FPS_DIRECTORY != m_new_dir_status) {
+
+        if(FPS_FILE == m_new_dir_status) {
+            if(m_callback->getOverwrite()) {    //Is a file & overwrite is set:
+                std::string fn = removeTrailingDirectorySlashFromPath(m_new_dir_path);
+                if(std::remove(fn.c_str()) != 0) {
+                    LOGE("std::remove(\"%s\") failed with errno:%s",
+                            m_new_dir_path.c_str(), strerror(errno));
+                    throw UnknownException(
+                            "Could not overwrite file in output directory");
+                }
+            } else {                            //Is a file & overwrite is not set:
+                LOGE("Failed to extract directory, "
+                        "file with the same name exists in output directory");
+                throw UnknownException("Failed to extract directory, "
+                        "file with the same name exists in output directory");
+            }
+        }
+
+        //Try to create new directory in output directory
+        if(mkdir(m_new_dir_path.c_str(), 0775) == -1) {
+            LOGE("Couldn't create new directory: %s errno:%s",
+                    m_new_dir_path.c_str(), strerror(errno));
+            throw UnknownException(
+                    "Could not create new directory in extract output directory");
+        }
+    }
+
+    LOGD("Set dir: [%s] access and modify to: %4d-%2d-%2d %2d:%2d:%2d", m_new_dir_path.c_str(),
+            m_file_info.tmu_date.tm_year,
+            m_file_info.tmu_date.tm_mon,
+            m_file_info.tmu_date.tm_mday,
+            m_file_info.tmu_date.tm_hour,
+            m_file_info.tmu_date.tm_min,
+            m_file_info.tmu_date.tm_sec);
+
+    // Directory already exists we only need to update time
+    changeFileAccessAndModifyDate(m_new_dir_path, m_file_info.tmu_date);
+
+    LOGD("Extracted directory entry: [%s]", m_new_dir_path.c_str());
+}
+
+bool UnZipExtractRequest::prepareOutputSubdirectory()
+{
+    LOGD("Entered");
+    //This zip entry points to file - verify that parent directory in output dir exists
+    if(FPS_DIRECTORY != m_new_dir_status) {
+        if(FPS_FILE == m_new_dir_status) {
+            LOGE("Path: %s is pointing to file not directory!",
+                    m_new_dir_path.c_str());
+            throw UnknownException("Failed to extract file from zip archive, "
+                    "output path is invalid");
+        }
+
+        //Try to create new directory in output directory
+        //TODO investigate 0775 (mode) - Filesystem assumed that file should have parent mode
+        if(mkdir(m_new_dir_path.c_str(), 0775) == -1) {
+            LOGW("couldn't create new directory: %s errno:%s",
+                    m_new_dir_path.c_str(), strerror(errno));
+            //TODO check why mkdir return -1 but directory is successfully created
+            //     throw UnknownException(
+            //     "Could not create new directory in extract output directory");
+        }
+    }
+
+    if(m_callback->isCanceled()) {
+        LOGD("Operation cancelled");
+        throw OperationCanceledException();
+    }
+
+    const FilePathStatus output_fstatus = getPathStatus(m_output_filepath);
+    if(FPS_NOT_EXIST != output_fstatus) {
+        if(!m_callback->getOverwrite()) {
+            LOGW("%s exists at output path: [%s], overwrite is set to FALSE",
+                    (FPS_DIRECTORY == output_fstatus ? "Directory" : "File"),
+                    m_output_filepath.c_str());
+
+            //Just skip this file - TODO: this should be documented in WIDL
+            return false;
+        } else {
+            if(FPS_DIRECTORY == output_fstatus) {
+                try {
+                    LOGW("STUB Not removing directory");
+                    //Filesystem::PathPtr path = Filesystem::Path::create(m_output_filepath);
+                    //Filesystem::NodePtr node = Filesystem::Node::resolve(path);
+                    //node->remove(Filesystem::OPT_RECURSIVE);
+                    Filesystem::External::removeDirectoryRecursive(m_output_filepath);
+
+                    LOGD("Removed directory: [%s]", m_output_filepath.c_str());
+                } catch(PlatformException& ex) {
+                    LOGE("Remove dir: [%s] failed with exception: %s:%s",
+                            m_output_filepath.c_str(),
+                            ex.name().c_str(), ex.message().c_str());
+                    throw UnknownException("Could not overwrite existing directory");
+                } catch (...) {
+                    LOGE("Remove dir: [%s] failed", m_output_filepath.c_str());
+                    throw UnknownException("Could not overwrite existing directory");
+                }
+            } //else {
+                //We will overwrite it with fopen
+            //}
+        }
+    }
+
+    return true;
+}
+
+void UnZipExtractRequest::handleFileEntry()
+{
+    LOGD("Entered");
+    if(!prepareOutputSubdirectory()) {
+        LOGE("File exists but overwrite is false");
+        throw InvalidModificationException("file already exists.");
+    }
+
+    int err = unzOpenCurrentFilePassword(m_owner.m_unzip,
+        NULL); //password is not supported yet therefore passing NULL
+    if (UNZ_OK != err) {
+        LOGE("ret: %d", err);
+        throwArchiveException(err, "unzOpenCurrentFilePassword()");
+    }
+
+    //We have successfully opened curent file, therefore we should close it later
+    m_close_unz_current_file = true;
+
+    const size_t buffer_size = m_owner.m_default_buffer_size;
+    m_buffer = new(std::nothrow) char[buffer_size];
+    if(!m_buffer) {
+        LOGE("Couldn't allocate buffer with size: %s",
+                bytesToReadableString(buffer_size).c_str());
+        throw UnknownException("Memory allocation failed");
+    }
+
+    m_output_file = fopen(m_output_filepath.c_str(), "wb");
+    if(!m_output_file) {
+        LOGE("Couldn't open output file: %s", m_output_filepath.c_str());
+        throw UnknownException("Could not create extracted file");
+    }
+    m_delete_output_file = true;
+
+    int read_size = 0;
+    bool marked_as_finished = false;
+
+    LOGD("Started extracting: [%s] uncompressed size: %d - %s", m_filename_inzip,
+            m_file_info.uncompressed_size,
+            bytesToReadableString(m_file_info.uncompressed_size).c_str());
+
+    ExtractAllProgressCallback* extract_callback = NULL;
+    if(m_callback->getCallbackType() == EXTRACT_ALL_PROGRESS_CALLBACK ||
+            m_callback->getCallbackType() == EXTRACT_ENTRY_PROGRESS_CALLBACK) {
+        extract_callback = static_cast<ExtractAllProgressCallback*>(m_callback);
+        extract_callback->startedExtractingFile(m_file_info.uncompressed_size);
+    }
+
+    ArchiveFileEntryPtrMapPtr entries = m_callback->getArchiveFile()->getEntryMap();
+    auto it = entries->find(m_filename_inzip);
+    if (it == entries->end()) {
+        LOGE("Entry not found");
+        throw NotFoundException("Entry not found");
+    }
+
+    while(true) {
+        if(m_callback->isCanceled()) {
+            LOGD("Operation cancelled");
+            throw OperationCanceledException();
+        }
+
+        read_size = unzReadCurrentFile(m_owner.m_unzip, m_buffer, buffer_size);
+        if (read_size < 0) {
+            LOGE("unzReadCurrentFile failed with error code:%d for file:%s", read_size,
+                    m_filename_inzip);
+            throw UnknownException("Failed to extract file from zip archive");
+        }
+        else if(0 == read_size) {
+
+            if(extract_callback) {
+                if(!marked_as_finished) {
+                    LOGD("NOT marked_as_finished -> increment extracted files counter");
+                    extract_callback->finishedExtractingFile();
+
+                    //Call progress callback only if we expected empty file
+                    if(m_file_info.uncompressed_size == 0) {
+                        LOGD("Calling progress callback(%f, %s)",
+                                extract_callback->getOverallProgress(), m_filename_inzip);
+                        extract_callback->callProgressCallbackOnMainThread(
+                                extract_callback->getOverallProgress(), it->second);
+                    }
+                }
+            }
+            //Finished writing file, we should not delete extracted file
+            m_delete_output_file = false;
+            break;
+        }
+
+        if (fwrite(m_buffer, read_size, 1, m_output_file) != 1) {
+            LOGE("Couldn't write extracted data to output file:%s",
+                    m_output_filepath.c_str());
+            throw UnknownException("Could not write extract file into output file");
+        }
+
+        if(extract_callback) {
+            extract_callback->extractedPartOfFile(read_size);
+            LOGD("File: [%s] extracted: %s - %f%%; total progress %f%%", m_filename_inzip,
+                    bytesToReadableString(read_size).c_str(),
+                    100.0f * extract_callback->getCurrentFileProgress(),
+                    100.0f * extract_callback->getOverallProgress());
+
+            // It is better to update number of extracted entries so we will have
+            // overal progres: 1.0 if all files are extracted
+            //
+            if(extract_callback->getCurrentFileProgress() >= 1.0) {
+                LOGD("Current file: [%s] progress: %f >= 1.0 -> "
+                        "marked_as_finished = true and increment extracted files counter",
+                        m_filename_inzip, extract_callback->getCurrentFileProgress());
+                marked_as_finished = true;
+                extract_callback->finishedExtractingFile();
+            }
+
+            LOGD("Calling progress callback(%f, %s)",
+                    extract_callback->getOverallProgress(), m_filename_inzip);
+            extract_callback->callProgressCallbackOnMainThread(
+                    extract_callback->getOverallProgress(), it->second);
+        }
+    }
+
+    if(m_output_file) {
+        fclose(m_output_file);
+        m_output_file = NULL;
+    }
+
+    changeFileAccessAndModifyDate(m_output_filepath, m_file_info.tmu_date);
+}
+
+} //namespace Archive
+} //namespace DeviceAPI
diff --git a/src/archive/un_zip_extract_request.h b/src/archive/un_zip_extract_request.h
new file mode 100644 (file)
index 0000000..3612021
--- /dev/null
@@ -0,0 +1,85 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef __TIZEN_ARCHIVE_UNZIP_EXTRACT_REQUEST_H__
+#define __TIZEN_ARCHIVE_UNZIP_EXTRACT_REQUEST_H__
+
+#include <stdio.h>
+#include <string>
+#include "un_zip.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+enum FilePathStatus {
+    FPS_NOT_EXIST = 0,
+    FPS_FILE = 1,
+    FPS_DIRECTORY = 2
+};
+
+class UnZipExtractRequest
+{
+public:
+    static void execute(UnZip& owner,
+            const std::string& extract_path,
+            const std::string& base_strip_path,
+            BaseProgressCallback* callback);
+
+    ~UnZipExtractRequest();
+
+private:
+    UnZipExtractRequest(UnZip& owner,
+            const std::string& extract_path,
+            const std::string& base_strip_path,
+            BaseProgressCallback* callback);
+    void run();
+
+    void getCurrentFileInfo();
+
+    void handleDirectoryEntry();
+
+    void handleFileEntry();
+    bool prepareOutputSubdirectory();
+
+    //-----------------------------------------------------------------------------
+    //Input request variables
+    UnZip& m_owner;
+    const std::string m_extract_path;
+    const std::string m_base_strip_path;
+    BaseProgressCallback* m_callback;
+
+    //-----------------------------------------------------------------------------
+    //Working variables
+    FILE* m_output_file; //Used to write extracted file into output directory
+    char* m_buffer; //Memory buffer passed between Minizip lib and fwrite function
+
+    bool m_delete_output_file;
+    bool m_close_unz_current_file;
+
+    unz_file_info m_file_info; //Informations about current archive entry (from minizip)
+    char m_filename_inzip[512]; //Name of archive file entry (from minizip)
+    std::string m_output_filepath; //Extracted output file name with full path
+
+    FilePathStatus m_new_dir_status;
+    bool m_is_directory_entry; //Is this request for directory
+    std::string m_new_dir_path;
+};
+
+} //namespace Archive
+} //namespace DeviceAPI
+
+#endif // __TIZEN_ARCHIVE_UNZIP_EXTRACT_REQUEST_H__
diff --git a/src/archive/zip.cc b/src/archive/zip.cc
new file mode 100644 (file)
index 0000000..26b536d
--- /dev/null
@@ -0,0 +1,143 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "zip.h"
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "common/logger.h"
+#include "common/platform_exception.h"
+#include "filesystem_file.h"
+
+#include "archive_file.h"
+#include "archive_utils.h"
+#include "crypt.h"
+#include "zip_add_request.h"
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+void Zip::generateZipFileInfo(const std::string& filename, zip_fileinfo& out_zi)
+{
+    memset(&out_zi, 0, sizeof(zip_fileinfo));
+
+    time_t tm_t = 0;
+    if (filename != "-") {
+        std::string name = filename;
+        if (name[name.length() - 1] == '/') {
+            name[name.length() - 1] = '\0';
+        }
+
+        struct stat s;
+        // not all systems allow stat'ing a file with / appended
+        if (stat(name.c_str(), &s) == 0) {
+            tm_t = s.st_mtime;
+        }
+    }
+
+    struct tm* filedate = localtime(&tm_t);
+    if(filedate) {
+        out_zi.tmz_date.tm_sec  = filedate->tm_sec;
+        out_zi.tmz_date.tm_min  = filedate->tm_min;
+        out_zi.tmz_date.tm_hour = filedate->tm_hour;
+        out_zi.tmz_date.tm_mday = filedate->tm_mday;
+        out_zi.tmz_date.tm_mon  = filedate->tm_mon ;
+        out_zi.tmz_date.tm_year = filedate->tm_year;
+    }
+}
+
+Zip::Zip(const std::string& filename, ZipOpenMode open_mode) :
+        m_zipfile_name(filename),
+        m_zip(NULL),
+        m_default_buffer_size(1024 * 1024)
+{
+    LOGD("Entered");
+
+    int append_mode = APPEND_STATUS_CREATE;
+    if(ZOM_CREATEAFTER == open_mode) {
+        append_mode = APPEND_STATUS_CREATEAFTER;
+    } else if(ZOM_ADDINZIP == open_mode) {
+        append_mode = APPEND_STATUS_ADDINZIP;
+    }
+    LOGD("append_mode: %d", append_mode);
+
+    m_zip = zipOpen(filename.c_str(), append_mode);
+    if(!m_zip) {
+        LOGE("zipOpen returned NULL!");
+        throw UnknownException("Opening/creating zip file failed");
+    }
+    m_is_open = true;
+}
+
+Zip::~Zip()
+{
+    close();
+}
+
+void Zip::close()
+{
+    LOGD("Entered");
+    if(!m_is_open) {
+        LOGD("Already closed - exiting.");
+        return;
+    }
+
+    int errclose = zipClose(m_zip, NULL);
+    m_zip = NULL;
+
+    if (errclose != ZIP_OK) {
+        LOGE("ret: %d", errclose);
+        throwArchiveException(errclose, "zipClose()");
+    }
+    m_is_open = false;
+}
+
+ZipPtr Zip::createNew(const std::string& filename)
+{
+    LOGD("Entered");
+    return ZipPtr(new Zip(filename, ZOM_CREATE));
+}
+
+ZipPtr Zip::open(const std::string& filename)
+{
+    LOGD("Entered");
+    return ZipPtr(new Zip(filename, ZOM_ADDINZIP));
+}
+
+void Zip::addFile(AddProgressCallback*& callback)
+{
+    LOGD("Entered");
+    if(!callback) {
+        LOGE("callback is NULL!");
+        throw UnknownException("Could not add file(-s) to archive");
+    }
+    if(!m_is_open) {
+        LOGE("Zip file not opened - exiting");
+        throw UnknownException("Could not add file(-s) to archive - zip file closed");
+    }
+
+    ZipAddRequest::execute(*this, callback);
+}
+
+} //namespace Archive
+} //namespace DeviceAPI
diff --git a/src/archive/zip.h b/src/archive/zip.h
new file mode 100644 (file)
index 0000000..5a65c1f
--- /dev/null
@@ -0,0 +1,76 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef __TIZEN_ARCHIVE_ZIP_H__
+#define __TIZEN_ARCHIVE_ZIP_H__
+
+#include <memory>
+#include <string>
+
+#include <zip.h>
+
+#include "archive_callback_data.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+
+class ZipAddRequest;
+
+class Zip;
+typedef std::shared_ptr<Zip> ZipPtr;
+
+class Zip
+{
+public:
+    static ZipPtr createNew(const std::string& filename);
+    static ZipPtr open(const std::string& filename);
+    ~Zip();
+
+    /**
+     * When request has finished callback is set to NULL and is
+     * deleted on main thread after calling all progress callbacks.
+     * If exception is thrown please delete callback.
+     */
+    void addFile(AddProgressCallback*& callback);
+
+    void close();
+
+private:
+    enum ZipOpenMode {
+        ZOM_CREATE,
+        ZOM_CREATEAFTER,
+        ZOM_ADDINZIP
+    };
+
+    Zip(const std::string& filename, ZipOpenMode open_mode);
+
+    static void generateZipFileInfo(const std::string& filename, zip_fileinfo& out_zi);
+
+    std::string m_zipfile_name;
+    zipFile m_zip;
+    size_t m_default_buffer_size;
+    bool m_is_open;
+
+
+    friend class ZipAddRequest;
+};
+
+} //namespace Archive
+} //namespace DeviceAPI
+
+#endif // __TIZEN_ARCHIVE_ZIP_H__
diff --git a/src/archive/zip_add_request.cc b/src/archive/zip_add_request.cc
new file mode 100644 (file)
index 0000000..6ad5c6f
--- /dev/null
@@ -0,0 +1,534 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#include "zip_add_request.h"
+
+#include "common/logger.h"
+#include "common/platform_exception.h"
+
+#include "archive_file.h"
+#include "archive_file_entry.h"
+#include "archive_utils.h"
+
+using namespace common;
+
+namespace DeviceAPI {
+namespace Archive {
+
+ZipAddRequest::ZipAddRequest(Zip& owner, AddProgressCallback*& callback) :
+        m_owner(owner),
+        m_callback(callback),
+        m_input_file(NULL),
+        m_buffer(NULL),
+        m_buffer_size(m_owner.m_default_buffer_size),
+        m_files_to_compress(0),
+        m_bytes_to_compress(0),
+        m_files_compressed(0),
+        m_bytes_compressed(0),
+        m_compression_level(0),
+        m_new_file_in_zip_opened(false)
+{
+}
+
+ZipAddRequest::~ZipAddRequest()
+{
+    if(m_input_file) {
+        fclose(m_input_file);
+        m_input_file = NULL;
+    }
+
+    delete [] m_buffer;
+    m_buffer = NULL;
+
+
+    if(m_new_file_in_zip_opened) {
+        int err = zipCloseFileInZip(m_owner.m_zip);
+        if (ZIP_OK != err) {
+            LOGE("%s",getArchiveLogMessage(err, "zipCloseFileInZip()").c_str());
+        }
+    }
+}
+
+void ZipAddRequest::execute(Zip& owner, AddProgressCallback*& callback)
+{
+    ZipAddRequest req(owner, callback);
+    req.run();
+}
+
+void ZipAddRequest::run()
+{
+    if(!m_callback) {
+        LOGE("m_callback is NULL");
+        throw UnknownException("Could not add file(-s) to archive");
+    }
+
+    if(!m_callback->getFileEntry()) {
+        LOGE("m_callback->getFileEntry() is NULL");
+        throw InvalidValuesException("Provided ArchiveFileEntry is not correct");
+    }
+
+    if(m_callback->isCanceled()) {
+        LOGD("Operation cancelled");
+        throw OperationCanceledException();
+    }
+
+    m_compression_level = m_callback->getFileEntry()->getCompressionLevel();
+    m_root_src_file = m_callback->getFileEntry()->getFile();
+    m_root_src_file_node = m_root_src_file->getNode();
+
+    //We just need read permission to list files in subdirectories
+    LOGW("STUB Not setting PERM_READ permissions");
+    //m_root_src_file_node->setPermissions(Filesystem::PERM_READ);
+
+    std::string src_basepath, src_name;
+    getBasePathAndName(m_root_src_file_node->getFullPath(), src_basepath,
+            src_name);
+
+    m_absoulte_path_to_extract = src_basepath;
+    LOGD("m_absoulte_path_to_extract: [%s]", m_absoulte_path_to_extract.c_str());
+
+    m_destination_path_in_zip = removeDuplicatedSlashesFromPath(
+            m_callback->getFileEntry()->getDestination());
+
+    // If we have destination set then we need to create all folders and sub folders
+    // inside this zip archive.
+    //
+    if(m_destination_path_in_zip.length() > 0) {
+        LOGD("destination is: [%s]", m_destination_path_in_zip.c_str());
+
+        for(size_t i = 0; i < m_destination_path_in_zip.length(); ++i) {
+            const char cur_char = m_destination_path_in_zip[i];
+
+            if( ((cur_char == '/' || cur_char == '\\') && i > 0 ) ||
+                (i == m_destination_path_in_zip.length() - 1)) {
+
+                //Extract left side with '/':
+                const std::string new_dir = m_destination_path_in_zip.substr(0, i + 1);
+
+                LOGD("Adding empty directory: [%s] to archive", new_dir.c_str());
+                addEmptyDirectoryToZipArchive(new_dir);
+            }
+        }
+    }
+
+    // Generate list of files and all subdirectories
+    //
+    Filesystem::NodeList all_sub_nodes;
+    addNodeAndSubdirsToList(m_root_src_file_node, all_sub_nodes);
+
+    if(m_callback->isCanceled()) {
+        LOGD("Operation cancelled");
+        throw OperationCanceledException();
+    }
+
+    // Calculate total size to be compressed
+    //
+    m_bytes_to_compress = 0;
+    m_files_to_compress = 0;
+
+    int i = 0;
+    for(auto it = all_sub_nodes.begin(); it != all_sub_nodes.end(); ++it, ++i) {
+
+        unsigned long long size = 0;
+        if((*it)->getType() == Filesystem::NT_FILE) {
+            size = (*it)->getSize();
+            m_bytes_to_compress += size;
+            ++m_files_to_compress;
+        }
+
+        LOGD("[%d] : [%s] --zip--> [%s] | size: %s", i, (*it)->getFullPath().c_str(),
+                getNameInZipArchiveFor(*it, m_callback->getFileEntry()->getStriped()).c_str(),
+                bytesToReadableString(size).c_str());
+    }
+
+    LOGD("m_files_to_compress: %d", m_files_to_compress);
+    LOGD("m_bytes_to_compress: %llu (%s)", m_bytes_to_compress,
+            bytesToReadableString(m_bytes_to_compress).c_str());
+
+    if(m_callback->isCanceled()) {
+        LOGD("Operation cancelled");
+        throw OperationCanceledException();
+    }
+
+    // Begin files compression
+    //
+    for(auto it = all_sub_nodes.begin(); it != all_sub_nodes.end(); ++it, ++i) {
+            addToZipArchive(*it);
+    }
+
+    m_callback->callSuccessCallbackOnMainThread();
+    m_callback = NULL;
+}
+
+void ZipAddRequest::addNodeAndSubdirsToList(Filesystem::NodePtr src_node,
+        Filesystem::NodeList& out_list_of_child_nodes)
+{
+    out_list_of_child_nodes.push_back(src_node);
+
+    if(Filesystem::NT_DIRECTORY == src_node->getType()) {
+        LOGW("STUB Not generating recursive list of files in directory");
+        //auto child_nodes = src_node->getChildNodes();
+        //for(auto it = child_nodes.begin(); it != child_nodes.end(); ++it) {
+        //    addNodeAndSubdirsToList(*it, out_list_of_child_nodes);
+        //}
+    }
+}
+
+void ZipAddRequest::addEmptyDirectoryToZipArchive(std::string name_in_zip)
+{
+    LOGD("Entered name_in_zip:%s", name_in_zip.c_str());
+
+    if(name_in_zip.length() == 0) {
+        LOGW("Trying to create directory with empty name - \"\"");
+        return;
+    }
+
+    const char last_char = name_in_zip[name_in_zip.length()-1];
+    if(last_char != '/' && last_char != '\\') {
+            name_in_zip += "/";
+            LOGD("Corrected name_in_zip: [%s]", name_in_zip.c_str());
+    }
+
+    if(m_new_file_in_zip_opened) {
+        LOGE("WARNING: Previous new file in zip archive is opened!");
+        int err = zipCloseFileInZip(m_owner.m_zip);
+        if (ZIP_OK != err) {
+            LOGE("%s",getArchiveLogMessage(err, "zipCloseFileInZip()").c_str());
+        }
+    }
+
+    bool is_directory = false;
+    std::string conflicting_name;
+
+    if(m_callback->getArchiveFile()->isEntryWithNameInArchive(name_in_zip,
+            &is_directory, &conflicting_name)) {
+
+        if(!is_directory) {
+            LOGE("Entry: [%s] exists and is NOT directory!", conflicting_name.c_str());
+
+            LOGE("Throwing InvalidValuesException - File with the same name exists");
+            throw InvalidValuesException("File with the same name exists");
+        }
+
+        LOGD("Directory: [%s] already exists -> nothing to do", name_in_zip.c_str());
+            return;
+    }
+
+    if(m_callback->isCanceled()) {
+        LOGD("Operation cancelled");
+        throw OperationCanceledException();
+    }
+
+    zip_fileinfo new_dir_info;
+    memset(&new_dir_info, 0, sizeof(zip_fileinfo));
+
+    //
+    // Since this directory does not exist we will set current time
+    //
+    time_t current_time = time(NULL);
+    struct tm* current_time_tm = localtime(&current_time);
+    if(current_time_tm) {
+        new_dir_info.tmz_date.tm_sec  = current_time_tm->tm_sec;
+        new_dir_info.tmz_date.tm_min  = current_time_tm->tm_min;
+        new_dir_info.tmz_date.tm_hour = current_time_tm->tm_hour;
+        new_dir_info.tmz_date.tm_mday = current_time_tm->tm_mday;
+        new_dir_info.tmz_date.tm_mon  = current_time_tm->tm_mon ;
+        new_dir_info.tmz_date.tm_year = current_time_tm->tm_year;
+    }
+
+    int err = zipOpenNewFileInZip3(m_owner.m_zip, name_in_zip.c_str(), &new_dir_info,
+            NULL, 0, NULL, 0, NULL,
+            (m_compression_level != 0) ? Z_DEFLATED : 0,
+            m_compression_level, 0,
+            -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
+            NULL, 0);
+
+    if (err != ZIP_OK) {
+        LOGE("ret: %d", err);
+        throwArchiveException(err, "zipOpenNewFileInZip3()");
+    }
+
+    m_new_file_in_zip_opened = true;
+
+    err = zipCloseFileInZip(m_owner.m_zip);
+    if (ZIP_OK != err) {
+        LOGE("%s",getArchiveLogMessage(err, "zipCloseFileInZip()").c_str());
+    }
+
+    LOGD("Added new empty directory to archive: [%s]", name_in_zip.c_str());
+    m_new_file_in_zip_opened = false;
+}
+
+void ZipAddRequest::addToZipArchive(Filesystem::NodePtr src_file_node)
+{
+    const std::string name_in_zip = getNameInZipArchiveFor(src_file_node,
+            m_callback->getFileEntry()->getStriped());
+    const std::string src_file_path = src_file_node->getFullPath();
+
+    LOGD("Compress: [%s] to zip archive as: [%s]", src_file_path.c_str(),
+            name_in_zip.c_str());
+
+    zip_fileinfo new_file_info;
+    Zip::generateZipFileInfo(src_file_path, new_file_info);
+
+    if(m_new_file_in_zip_opened) {
+        LOGE("WARNING: Previous new file in zip archive is opened!");
+        int err = zipCloseFileInZip(m_owner.m_zip);
+        if (ZIP_OK != err) {
+            LOGE("%s",getArchiveLogMessage(err, "zipCloseFileInZip()").c_str());
+        }
+    }
+
+    if(m_callback->isCanceled()) {
+        LOGD("Operation cancelled");
+        throw OperationCanceledException();
+    }
+
+    std::string conflicting_name;
+    if(m_callback->getArchiveFile()->isEntryWithNameInArchive(name_in_zip,
+            NULL, &conflicting_name)) {
+
+        LOGE("Cannot add new entry with name name: [%s] "
+                "it would conflict with existing entry: [%s]",
+                name_in_zip.c_str(), conflicting_name.c_str());
+
+        LOGE("Throwing InvalidModificationException - Archive entry name conflicts");
+        throw InvalidModificationException("Archive entry name conflicts");
+    }
+
+    int err = zipOpenNewFileInZip3(m_owner.m_zip, name_in_zip.c_str(), &new_file_info,
+            NULL, 0, NULL, 0, NULL,
+            (m_compression_level != 0) ? Z_DEFLATED : 0,
+            m_compression_level, 0,
+            -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
+            NULL, 0);
+
+    if (err != ZIP_OK) {
+        LOGE("ret: %d", err);
+        throwArchiveException(err, "zipOpenNewFileInZip3()");
+    }
+
+    m_new_file_in_zip_opened = true;
+
+    if(m_input_file) {
+        LOGW("WARNING: Previous m_input_file has not been closed");
+        fclose(m_input_file);
+        m_input_file = NULL;
+    }
+
+    //Filesystem::File::PermissionList perm_list;
+    //Filesystem::FilePtr cur_file(new Filesystem::File(src_file_node, perm_list));
+
+    LOGW("STUB ignore src_file_node and PermissionList");
+    Filesystem::FilePtr cur_file(new Filesystem::File(src_file_path));
+    ArchiveFileEntryPtr cur_afentry(new ArchiveFileEntry(cur_file));
+    cur_afentry->setCompressionLevel(m_compression_level);
+    cur_afentry->setName(name_in_zip);
+
+    LOGW("STUB Not setting Modified");
+    //cur_afentry->setModified(src_file_node->getModified());
+
+    auto entry = m_callback->getFileEntry();
+    cur_afentry->setDestination(entry->getDestination());
+    cur_afentry->setStriped(entry->getStriped());
+    cur_afentry->setSize(0);
+
+    LOGD("m_bytes_compressed:%llu / m_bytes_to_compress: %llu",
+            m_bytes_compressed, m_bytes_to_compress);
+
+    LOGD("m_files_compressed:%d / m_files_to_compress: %d",
+            m_files_compressed, m_files_to_compress);
+
+    if(src_file_node->getType() == Filesystem::NT_FILE) {
+        m_input_file = fopen(src_file_path.c_str(), "rb");
+        if (!m_input_file) {
+            LOGE("Error opening source file:%s", src_file_path.c_str());
+            throw UnknownException("Could not open file to be added");
+        }
+
+        //Get file length
+        fseek(m_input_file, 0, SEEK_END);
+        const size_t in_file_size = ftell(m_input_file);
+        fseek(m_input_file, 0, SEEK_SET);
+        LOGD("Source file: [%s] size: %d - %s", src_file_path.c_str(),
+                in_file_size,
+                bytesToReadableString(in_file_size).c_str());
+
+        cur_afentry->setSize(in_file_size);
+
+        if(!m_buffer) {
+            m_buffer = new(std::nothrow) char[m_buffer_size];
+            if(!m_buffer) {
+                LOGE("Couldn't allocate m_buffer");
+                throw UnknownException("Memory allocation error");
+            }
+        }
+
+        size_t total_bytes_read = 0;
+        size_t size_read = 0;
+
+        do {
+            size_read = fread(m_buffer, 1, m_buffer_size, m_input_file);
+            if (size_read < m_buffer_size &&
+                        feof(m_input_file) == 0) {
+                LOGE("Error reading source file: %s\n", src_file_path.c_str());
+                throw UnknownException("New file addition failed");
+            }
+
+            LOGD("Read: %d bytes from input file:[%s]", size_read,
+                    src_file_path.c_str());
+            total_bytes_read += size_read;
+            m_bytes_compressed += size_read;
+
+            if (size_read > 0) {
+                err = zipWriteInFileInZip (m_owner.m_zip, m_buffer, size_read);
+                if (err < 0) {
+                    LOGE("Error during adding file: %s into zip archive",
+                            src_file_path.c_str());
+                    throw UnknownException("New file addition failed");
+                }
+            }
+
+            if(total_bytes_read == in_file_size) {
+                LOGD("Finished reading and compressing source file: [%s]",
+                        src_file_path.c_str());
+                ++m_files_compressed;
+            }
+
+            LOGD("Callculatting overall progress: %llu/%llu bytes; "
+                    "%d/%d files; current file: [%s] progress: %d/%d bytes; ",
+                    m_bytes_compressed, m_bytes_to_compress,
+                    m_files_compressed, m_files_to_compress,
+                    src_file_path.c_str(),
+                    total_bytes_read, in_file_size);
+
+            double progress = 1.0;
+            if(m_bytes_to_compress > 0 || m_files_to_compress > 0) {
+                progress = static_cast<double>(m_bytes_compressed + m_files_compressed) /
+                        static_cast<double>(m_bytes_to_compress + m_files_to_compress);
+            }
+
+            LOGD("Wrote: %s total progress: %.2f%% %d/%d files",
+                    bytesToReadableString(size_read).c_str(), progress * 100.0,
+                    m_files_compressed, m_files_to_compress);
+
+            LOGD("Calling add onprogress callback(%f, %s)", progress,
+                    name_in_zip.c_str());
+            m_callback->callProgressCallbackOnMainThread(progress, cur_afentry);
+
+        } while (size_read > 0 && total_bytes_read < in_file_size);
+
+        if(in_file_size != total_bytes_read) {
+            LOGE("in_file_size(%d) != total_bytes_read(%d)", in_file_size,
+                    total_bytes_read);
+            throw UnknownException("Could not add file to archive");
+        }
+
+        fclose(m_input_file);
+        m_input_file = NULL;
+    }
+
+    err = zipCloseFileInZip(m_owner.m_zip);
+    if (ZIP_OK != err) {
+        LOGE("%s",getArchiveLogMessage(err, "zipCloseFileInZip()").c_str());
+    }
+
+    m_new_file_in_zip_opened = false;
+}
+
+std::string removeDirCharsFromFront(const std::string& path)
+{
+    for(size_t i = 0; i < path.length(); ++i) {
+        const char& cur = path[i];
+        if(cur != '/' && cur != '\\') {
+            return path.substr(i);
+        }
+    }
+
+    return std::string();   //in case path contained only slashes
+}
+
+std::string generateFullPathForZip(const std::string& path)
+{
+    //Step 1: Remove / from begining
+    const size_t path_len = path.length();
+
+    size_t start_i = 0;
+    for(size_t i = 0; i < path_len; ++i) {
+        const char& cur = path[i];
+        if(cur != '/' && cur != '\\') {
+            start_i = i;
+            break;
+        }
+    }
+
+    std::string out;
+    out.reserve(path_len);
+
+    //Step 1: Remove duplicated / characters
+    bool prev_is_dir = false;
+    for(size_t i = start_i; i < path_len; ++i) {
+        const char& cur = path[i];
+        if(cur == '/' || cur == '\\') {
+            if(!prev_is_dir) {
+                out += cur;
+            }
+            prev_is_dir = true;
+        } else {
+            prev_is_dir = false;
+            out += cur;
+        }
+    }
+
+    return out;
+}
+
+std::string ZipAddRequest::getNameInZipArchiveFor(Filesystem::NodePtr node, bool strip)
+{
+    const std::string node_full_path = node->getPath()->getFullPath();
+    std::string cut_path;
+
+    const size_t pos = node_full_path.find(m_absoulte_path_to_extract);
+    if(std::string::npos == pos) {
+        LOGW("node: [%s] is not containing m_absoulte_path_to_extract: [%s]!",
+                node_full_path.c_str(),
+                m_absoulte_path_to_extract.c_str());
+    }
+
+    cut_path = node_full_path.substr(pos+m_absoulte_path_to_extract.length());
+    LOGD("node_full_path:%s cut_path: %s", node_full_path.c_str(), cut_path.c_str());
+
+    if(!strip) {
+        cut_path = m_callback->getBaseVirtualPath() + "/" + cut_path;
+        LOGD("nonstripped cut_path: %s", cut_path.c_str());
+    }
+
+    std::string name = generateFullPathForZip(m_destination_path_in_zip + "/" + cut_path);
+    if(node->getType() == Filesystem::NT_DIRECTORY) {
+        if(name.length() > 0
+                && name[name.length()-1] != '/'
+                && name[name.length()-1] != '\\') {
+            name += "/";
+            LOGD("Directory: [%s] added \\", name.c_str());
+        }
+    }
+
+    return name;
+}
+
+} //namespace Archive
+} //namespace DeviceAPI
diff --git a/src/archive/zip_add_request.h b/src/archive/zip_add_request.h
new file mode 100644 (file)
index 0000000..081ba8c
--- /dev/null
@@ -0,0 +1,86 @@
+//
+// Tizen Web Device API
+// Copyright (c) 2014 Samsung Electronics Co., Ltd.
+//
+// Licensed under the Apache License, Version 2.0 (the License);
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#ifndef __TIZEN_ARCHIVE_ZIP_ADD_REQUEST_H__
+#define __TIZEN_ARCHIVE_ZIP_ADD_REQUEST_H__
+
+#include <stdio.h>
+#include <string>
+
+#include "filesystem_file.h"
+
+#include "archive_callback_data.h"
+#include "zip.h"
+
+namespace DeviceAPI {
+namespace Archive {
+
+class ZipAddRequest
+{
+public:
+
+    /**
+     * When request has finished callback is set to NULL and is
+     * deleted on main thread after calling all progress callbacks.
+     * If exception is thrown please delete callback.
+     */
+    static void execute(Zip& owner, AddProgressCallback*& callback);
+    ~ZipAddRequest();
+
+private:
+    ZipAddRequest(Zip& owner, AddProgressCallback*& callback);
+    void run();
+
+    void addNodeAndSubdirsToList(Filesystem::NodePtr src_node,
+            Filesystem::NodeList& out_list_of_child_nodes);
+
+    void addEmptyDirectoryToZipArchive(std::string name_in_zip);
+    void addToZipArchive(Filesystem::NodePtr src_file_node);
+
+    std::string getNameInZipArchiveFor(Filesystem::NodePtr node, bool strip);
+
+    //-----------------------------------------------------------------------------
+    //Input request variables
+    Zip& m_owner;
+    AddProgressCallback* m_callback;
+
+
+    FILE* m_input_file;
+    char* m_buffer;
+    size_t m_buffer_size;
+
+
+    unsigned long m_files_to_compress;
+    unsigned long long m_bytes_to_compress;
+
+    unsigned long m_files_compressed;
+    unsigned long long m_bytes_compressed;
+
+    Filesystem::FilePtr m_root_src_file;
+    Filesystem::NodePtr m_root_src_file_node;
+    std::string m_absoulte_path_to_extract;
+    std::string m_destination_path_in_zip;
+
+    unsigned int m_compression_level;
+
+    bool m_new_file_in_zip_opened;
+};
+
+} //namespace Archive
+} //namespace DeviceAPI
+
+#endif // __TIZEN_ARCHIVE_ZIP_ADD_REQUEST_H__
index 96d8cb6..6c2df9d 100644 (file)
@@ -44,6 +44,8 @@ DEFINE_EXCEPTION(NotFound)
 DEFINE_EXCEPTION(InvalidAccess)
 DEFINE_EXCEPTION(Abort)
 DEFINE_EXCEPTION(QuotaExceeded)
+DEFINE_EXCEPTION(InvalidState)
+DEFINE_EXCEPTION(InvalidModification)
 #undef DEFINE_EXCEPTION
 
 } // namespace common