diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8497dbe8c2b5a7271ad351e434d575f5910d8d1b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+*.pro.user
+lib/qtlib/
diff --git a/README.md b/README.md
index d9888c9591a1a82f4fb53d9ae50a19461d92ed9c..2f680753d252a5f32228b30fb1365b77680a89cf 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,10 @@
-# ocs-lib
-a shared library to handle filemanagement and "apply"
+# ocs-manager
+
+[![License: LGPL v3](https://img.shields.io/badge/License-LGPL%20v3-blue.svg)](https://www.gnu.org/licenses/lgpl-3.0)
+[![Build Status](https://travis-ci.org/opendesktop/ocs-manager.svg?branch=master)](https://travis-ci.org/opendesktop/ocs-manager)
+
+A management tool for ocs.
+
+Copyright: 2017, Opendesktop.org
+
+License: LGPL-3+
diff --git a/app/app.pri b/app/app.pri
new file mode 100644
index 0000000000000000000000000000000000000000..007ea3671b589a5f7d6bb913cc28d0bb02a12c24
--- /dev/null
+++ b/app/app.pri
@@ -0,0 +1,26 @@
+QT += \
+    core \
+    network
+
+QT -= gui
+
+HEADERS += \
+    $${PWD}/src/handlers/confighandler.h \
+    $${PWD}/src/handlers/systemhandler.h \
+    $${PWD}/src/handlers/ocshandler.h \
+    $${PWD}/src/handlers/itemhandler.h
+
+SOURCES += \
+    $${PWD}/src/main.cpp \
+    $${PWD}/src/handlers/confighandler.cpp \
+    $${PWD}/src/handlers/systemhandler.cpp \
+    $${PWD}/src/handlers/ocshandler.cpp \
+    $${PWD}/src/handlers/itemhandler.cpp
+
+RESOURCES += $${PWD}/configs/configs.qrc
+
+INCLUDEPATH += $${PWD}/src
+
+unix:!ios:!android {
+    QT += dbus
+}
diff --git a/app/configs/application.json b/app/configs/application.json
new file mode 100644
index 0000000000000000000000000000000000000000..b61c09fd7cd3497720ebd33e1ad0a62f45cbe240
--- /dev/null
+++ b/app/configs/application.json
@@ -0,0 +1,13 @@
+{
+    "id": "ocs-manager",
+    "name": "ocs-manager",
+    "version": "0.0.0",
+    "organization": "Opendesktop.org",
+    "domain": "org.opendesktop.ocs-manager",
+    "icon": "",
+    "description": "A management tool for ocs.",
+    "license": "LGPL-3+",
+    "author": "Opendesktop.org",
+    "contact": "contact@opendesktop.org",
+    "homepage": "https://github.com/opendesktop/ocs-manager"
+}
diff --git a/app/configs/configs.qrc b/app/configs/configs.qrc
new file mode 100644
index 0000000000000000000000000000000000000000..496e259d7a5937ae4a51fa761a76b84b33a05daf
--- /dev/null
+++ b/app/configs/configs.qrc
@@ -0,0 +1,7 @@
+<RCC>
+    <qresource prefix="/configs">
+        <file>application.json</file>
+        <file>install_types.json</file>
+        <file>install_types_alias.json</file>
+    </qresource>
+</RCC>
diff --git a/app/configs/install_types.json b/app/configs/install_types.json
new file mode 100644
index 0000000000000000000000000000000000000000..335de22350e81310a3820d1f0d79e6a5aea33037
--- /dev/null
+++ b/app/configs/install_types.json
@@ -0,0 +1,192 @@
+{
+    "bin": {
+        "name": "Softwares",
+        "destination": "$HOME/.local/bin",
+        "generic_destination": "$APP_DATA/bin"
+    },
+    "downloads": {
+        "name": "Downloads",
+        "destination": "$HOME/Downloads",
+        "generic_destination": "$APP_DATA/downloads"
+    },
+    "documents": {
+        "name": "Documents",
+        "destination": "$HOME/Documents",
+        "generic_destination": "$APP_DATA/documents"
+    },
+    "pictures": {
+        "name": "Pictures",
+        "destination": "$HOME/Pictures",
+        "generic_destination": "$APP_DATA/pictures"
+    },
+    "music": {
+        "name": "Music",
+        "destination": "$HOME/Music",
+        "generic_destination": "$APP_DATA/music"
+    },
+    "videos": {
+        "name": "Videos",
+        "destination": "$HOME/Videos",
+        "generic_destination": "$APP_DATA/videos"
+    },
+    "wallpapers": {
+        "name": "Wallpapers",
+        "destination": "$XDG_DATA_HOME/wallpapers",
+        "generic_destination": "$APP_DATA/wallpapers"
+    },
+    "fonts": {
+        "name": "Fonts",
+        "destination": "$HOME/.fonts",
+        "generic_destination": "$APP_DATA/fonts"
+    },
+    "cursors": {
+        "name": "Cursors",
+        "destination": "$HOME/.icons",
+        "generic_destination": "$APP_DATA/cursors"
+    },
+    "icons": {
+        "name": "Icons",
+        "destination": "$XDG_DATA_HOME/icons",
+        "generic_destination": "$APP_DATA/icons"
+    },
+    "emoticons": {
+        "name": "Emoticons",
+        "destination": "$XDG_DATA_HOME/emoticons",
+        "generic_destination": "$APP_DATA/emoticons"
+    },
+    "themes": {
+        "name": "Desktop Themes",
+        "destination": "$HOME/.themes",
+        "generic_destination": "$APP_DATA/themes"
+    },
+    "emerald_themes": {
+        "name": "Emerald Themes",
+        "destination": "$HOME/.emerald/themes",
+        "generic_destination": "$APP_DATA/emerald_themes"
+    },
+    "enlightenment_themes": {
+        "name": "Enlightenment Themes",
+        "destination": "$HOME/.e/e/themes",
+        "generic_destination": "$APP_DATA/enlightenment_themes"
+    },
+    "enlightenment_backgrounds": {
+        "name": "Enlightenment Backgrounds",
+        "destination": "$HOME/.e/e/backgrounds",
+        "generic_destination": "$APP_DATA/enlightenment_backgrounds"
+    },
+    "fluxbox_styles": {
+        "name": "Fluxbox Styles",
+        "destination": "$HOME/.fluxbox/styles",
+        "generic_destination": "$APP_DATA/fluxbox_styles"
+    },
+    "pekwm_themes": {
+        "name": "PekWM Themes",
+        "destination": "$HOME/.pekwm/themes",
+        "generic_destination": "$APP_DATA/pekwm_themes"
+    },
+    "icewm_themes": {
+        "name": "IceWM Themes",
+        "destination": "$HOME/.icewm/themes",
+        "generic_destination": "$APP_DATA/icewm_themes"
+    },
+    "plasma_plasmoids": {
+        "name": "Plasma Plasmoids",
+        "destination": "$XDG_DATA_HOME/plasma/plasmoids",
+        "generic_destination": "$APP_DATA/plasma_plasmoids"
+    },
+    "plasma_look_and_feel": {
+        "name": "Plasma Look and Feel",
+        "destination": "$XDG_DATA_HOME/plasma/look-and-feel",
+        "generic_destination": "$APP_DATA/plasma_look_and_feel"
+    },
+    "plasma_desktopthemes": {
+        "name": "Plasma Desktop Themes",
+        "destination": "$XDG_DATA_HOME/plasma/desktoptheme",
+        "generic_destination": "$APP_DATA/plasma_desktopthemes"
+    },
+    "kwin_effects": {
+        "name": "KWin Effects",
+        "destination": "$XDG_DATA_HOME/kwin/effects",
+        "generic_destination": "$APP_DATA/kwin_effects"
+    },
+    "kwin_scripts": {
+        "name": "KWin Scripts",
+        "destination": "$XDG_DATA_HOME/kwin/scripts",
+        "generic_destination": "$APP_DATA/kwin_scripts"
+    },
+    "kwin_tabbox": {
+        "name": "KWin Window Switcher",
+        "destination": "$XDG_DATA_HOME/kwin/tabbox",
+        "generic_destination": "$APP_DATA/kwin_tabbox"
+    },
+    "aurorae_themes": {
+        "name": "Aurorae Themes",
+        "destination": "$XDG_DATA_HOME/aurorae/themes",
+        "generic_destination": "$APP_DATA/aurorae_themes"
+    },
+    "dekorator_themes": {
+        "name": "deKorator Themes",
+        "destination": "$XDG_DATA_HOME/deKorator/themes",
+        "generic_destination": "$APP_DATA/dekorator_themes"
+    },
+    "qtcurve": {
+        "name": "QtCurve Themes",
+        "destination": "$XDG_DATA_HOME/QtCurve",
+        "generic_destination": "$APP_DATA/qtcurve"
+    },
+    "color_schemes": {
+        "name": "KDE Color Schemes",
+        "destination": "$XDG_DATA_HOME/color-schemes",
+        "generic_destination": "$APP_DATA/color_schemes"
+    },
+    "gnome_shell_extensions": {
+        "name": "Gnome Shell Extensions",
+        "destination": "$XDG_DATA_HOME/gnome-shell/extensions",
+        "generic_destination": "$APP_DATA/gnome_shell_extensions"
+    },
+    "cinnamon_applets": {
+        "name": "Cinnamon Applets",
+        "destination": "$XDG_DATA_HOME/cinnamon/applets",
+        "generic_destination": "$APP_DATA/cinnamon_applets"
+    },
+    "cinnamon_desklets": {
+        "name": "Cinnamon Desklets",
+        "destination": "$XDG_DATA_HOME/cinnamon/desklets",
+        "generic_destination": "$APP_DATA/cinnamon_desklets"
+    },
+    "cinnamon_extensions": {
+        "name": "Cinnamon Extensions",
+        "destination": "$XDG_DATA_HOME/cinnamon/extensions",
+        "generic_destination": "$APP_DATA/cinnamon_extensions"
+    },
+    "nautilus_scripts": {
+        "name": "Nautilus Scripts",
+        "destination": "$XDG_DATA_HOME/nautilus/scripts",
+        "generic_destination": "$APP_DATA/nautilus_scripts"
+    },
+    "amarok_scripts": {
+        "name": "Amarok Scripts",
+        "destination": "$KDEHOME/share/apps/amarok/scripts",
+        "generic_destination": "$APP_DATA/amarok_scripts"
+    },
+    "yakuake_skins": {
+        "name": "Yakuake Skins",
+        "destination": "$KDEHOME/share/apps/yakuake/skins",
+        "generic_destination": "$APP_DATA/yakuake_skins"
+    },
+    "cairo_clock_themes": {
+        "name": "Cairo-Clock Themes",
+        "destination": "$HOME/.cairo-clock/themes",
+        "generic_destination": "$APP_DATA/cairo_clock_themes"
+    },
+    "books": {
+        "name": "Books",
+        "destination": "$APP_DATA/books",
+        "generic_destination": "$APP_DATA/books"
+    },
+    "comics": {
+        "name": "Comics",
+        "destination": "$APP_DATA/comics",
+        "generic_destination": "$APP_DATA/comics"
+    }
+}
diff --git a/app/configs/install_types_alias.json b/app/configs/install_types_alias.json
new file mode 100644
index 0000000000000000000000000000000000000000..d67bb8bdd3d57e9ca16d759627fb6e2babed23ea
--- /dev/null
+++ b/app/configs/install_types_alias.json
@@ -0,0 +1,62 @@
+{
+    "gnome_shell_themes": {
+        "base": "themes",
+        "name": "Gnome Shell Themes"
+    },
+    "cinnamon_themes": {
+        "base": "themes",
+        "name": "Cinnamon Themes"
+    },
+    "gtk2_themes": {
+        "base": "themes",
+        "name": "GTK2 Themes"
+    },
+    "gtk3_themes": {
+        "base": "themes",
+        "name": "GTK3 Themes"
+    },
+    "metacity_themes": {
+        "base": "themes",
+        "name": "Metacity Themes"
+    },
+    "xfwm4_themes": {
+        "base": "themes",
+        "name": "XFWM4 Themes"
+    },
+    "openbox_themes": {
+        "base": "themes",
+        "name": "Openbox Themes"
+    },
+    "kvantum_themes": {
+        "base": "themes",
+        "name": "Kvantum Themes"
+    },
+    "compiz_themes": {
+        "base": "emerald_themes",
+        "name": "Compiz Themes"
+    },
+    "beryl_themes": {
+        "base": "emerald_themes",
+        "name": "Beryl Themes"
+    },
+    "plasma4_plasmoids": {
+        "base": "plasma_plasmoids",
+        "name": "Plasma4 Plasmoids"
+    },
+    "plasma5_plasmoids": {
+        "base": "plasma_plasmoids",
+        "name": "Plasma5 Plasmoids"
+    },
+    "plasma5_look_and_feel": {
+        "base": "plasma_look_and_feel",
+        "name": "Plasma5 Look and Feel"
+    },
+    "plasma5_desktopthemes": {
+        "base": "plasma_desktopthemes",
+        "name": "Plasma5 Desktop Themes"
+    },
+    "plasma_color_schemes": {
+        "base": "color_schemes",
+        "name": "Plasma Color Schemes"
+    }
+}
diff --git a/app/src/handlers/confighandler.cpp b/app/src/handlers/confighandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2967180853462d3e081edf7e67fea6b0d079d07
--- /dev/null
+++ b/app/src/handlers/confighandler.cpp
@@ -0,0 +1,207 @@
+#include "confighandler.h"
+
+#include <QStringList>
+
+#include "qtlib_dir.h"
+
+ConfigHandler::ConfigHandler(QObject *parent)
+    : QObject(parent)
+{
+    appConfig_ = qtlib::Config(":/configs");
+    usrConfig_ = qtlib::Config(qtlib::Dir::genericConfigPath() + "/" + getAppConfigApplication()["id"].toString());
+}
+
+QJsonObject ConfigHandler::getAppConfigApplication()
+{
+    if (appConfigApplication_.isEmpty()) {
+        appConfigApplication_ = appConfig_.get("application");
+    }
+    return appConfigApplication_;
+}
+
+QJsonObject ConfigHandler::getAppConfigInstallTypes()
+{
+    if (appConfigInstallTypes_.isEmpty()) {
+        QJsonObject installTypes = appConfig_.get("install_types");
+        foreach (const QString &key, installTypes.keys()) {
+            QJsonObject installtype = installTypes[key].toObject();
+            installtype["destination"] = convertPathString(installtype["destination"].toString());
+            installtype["generic_destination"] = convertPathString(installtype["generic_destination"].toString());
+            installTypes[key] = installtype;
+        }
+        QJsonObject installTypesAlias = appConfig_.get("install_types_alias");
+        foreach (const QString &key, installTypesAlias.keys()) {
+            QJsonObject installTypeAlias = installTypesAlias[key].toObject();
+            QString baseKey = installTypeAlias["base"].toString();
+            if (installTypes.contains(baseKey)) {
+                QJsonObject installType = installTypes[baseKey].toObject();
+                installType["base"] = baseKey;
+                installType["name"] = installTypeAlias["name"].toString();
+                installTypes[key] = installType;
+            }
+        }
+        appConfigInstallTypes_ = installTypes;
+    }
+    return appConfigInstallTypes_;
+}
+
+QJsonObject ConfigHandler::getAppConfigFeaturedProviders()
+{
+    return appConfig_.get("featured_providers");
+}
+
+QJsonObject ConfigHandler::getUsrConfigApplication()
+{
+    return usrConfig_.get("application");
+}
+
+bool ConfigHandler::setUsrConfigApplication(const QJsonObject &object)
+{
+    return usrConfig_.set("application", object);
+}
+
+QJsonObject ConfigHandler::getUsrConfigProviders()
+{
+    return usrConfig_.get("providers");
+}
+
+bool ConfigHandler::setUsrConfigProviders(const QJsonObject &object)
+{
+    return usrConfig_.set("providers", object);
+}
+
+QJsonObject ConfigHandler::getUsrConfigCategories()
+{
+    return usrConfig_.get("categories");
+}
+
+bool ConfigHandler::setUsrConfigCategories(const QJsonObject &object)
+{
+    return usrConfig_.set("categories", object);
+}
+
+QJsonObject ConfigHandler::getUsrConfigInstalledItems()
+{
+    return usrConfig_.get("installed_items");
+}
+
+bool ConfigHandler::setUsrConfigInstalledItems(const QJsonObject &object)
+{
+    return usrConfig_.set("installed_items", object);
+}
+
+bool ConfigHandler::setUsrConfigProvidersProvider(const QString &providerKey, const QJsonObject &object)
+{
+    /* object format
+    {
+        "id": "example",
+        "location": "https://example.com/ocs/v1/",
+        "name": "Example",
+        "icon": "https://example.com/icon.png",
+        "termsofuse": "https://example.com/termsofuse",
+        "register": "https://example.com/register",
+        "_providerfile": "https://example.com/ocs/providers.xml"
+    }
+    */
+    QJsonObject providers = getUsrConfigProviders();
+    providers[providerKey] = object;
+    return setUsrConfigProviders(providers);
+}
+
+bool ConfigHandler::removeUsrConfigProvidersProvider(const QString &providerKey)
+{
+    QJsonObject providers = getUsrConfigProviders();
+    providers.remove(providerKey);
+    return setUsrConfigProviders(providers);
+}
+
+bool ConfigHandler::setUsrConfigCategoriesProvider(const QString &providerKey, const QJsonObject &object)
+{
+    /* object format
+    {
+        "1": {
+            "id": "1",
+            "name": "Desktop Icons",
+            "install_type": "icons"
+        },
+        "2": {
+            "id": "2",
+            "name": "Desktop Themes",
+            "install_type": "themes"
+        }
+    }
+    */
+    QJsonObject categories = getUsrConfigCategories();
+    categories[providerKey] = object;
+    return setUsrConfigCategories(categories);
+}
+
+bool ConfigHandler::removeUsrConfigCategoriesProvider(const QString &providerKey)
+{
+    QJsonObject categories = getUsrConfigCategories();
+    categories.remove(providerKey);
+    return setUsrConfigCategories(categories);
+}
+
+bool ConfigHandler::setUsrConfigCategoriesInstallType(const QString &providerKey, const QString &categoryKey, const QString &installType)
+{
+    QJsonObject categories = getUsrConfigCategories();
+    QJsonObject providerCategories;
+    if (categories.contains(providerKey)) {
+        providerCategories = categories[providerKey].toObject();
+    }
+    QJsonObject providerCategory;
+    if (providerCategories.contains(categoryKey)) {
+        providerCategory = providerCategories[categoryKey].toObject();
+    }
+    providerCategory["install_type"] = installType;
+    providerCategories[categoryKey] = providerCategory;
+    categories[providerKey] = providerCategories;
+    return setUsrConfigCategories(categories);
+}
+
+bool ConfigHandler::setUsrConfigInstalledItemsItem(const QString &itemKey, const QJsonObject &object)
+{
+    /* object format
+    {
+        "url": "http://example.com/downloads/123-1.tar.gz",
+        "filename": "123-1.tar.gz",
+        "install_type": "icons",
+        "provider": "http://example.com/ocs/v1/",
+        "content_id": "123",
+        "files": [
+            "iconset-light",
+            "iconset-dark"
+        ],
+        "installed_at": 1483658977219
+    }
+    */
+    QJsonObject installedItems = getUsrConfigInstalledItems();
+    installedItems[itemKey] = object;
+    return setUsrConfigInstalledItems(installedItems);
+}
+
+bool ConfigHandler::removeUsrConfigInstalledItemsItem(const QString &itemKey)
+{
+    QJsonObject installedItems = getUsrConfigInstalledItems();
+    installedItems.remove(itemKey);
+    return setUsrConfigInstalledItems(installedItems);
+}
+
+QString ConfigHandler::convertPathString(const QString &path)
+{
+    QString newPath = path;
+    if (newPath.contains("$HOME")) {
+        newPath.replace("$HOME", qtlib::Dir::homePath());
+    }
+    else if (newPath.contains("$XDG_DATA_HOME")) {
+        newPath.replace("$XDG_DATA_HOME", qtlib::Dir::genericDataPath());
+    }
+    else if (newPath.contains("$KDEHOME")) {
+        newPath.replace("$KDEHOME", qtlib::Dir::kdehomePath());
+    }
+    else if (newPath.contains("$APP_DATA")) {
+        newPath.replace("$APP_DATA", qtlib::Dir::genericDataPath() + "/" + getAppConfigApplication()["id"].toString());
+    }
+    return newPath;
+}
diff --git a/app/src/handlers/confighandler.h b/app/src/handlers/confighandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..cc38a18493ab3ab2180de6c3feaf589be7355e99
--- /dev/null
+++ b/app/src/handlers/confighandler.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include <QObject>
+#include <QJsonObject>
+
+#include "qtlib_config.h"
+
+class ConfigHandler : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit ConfigHandler(QObject *parent = 0);
+
+public slots:
+    QJsonObject getAppConfigApplication();
+    QJsonObject getAppConfigInstallTypes();
+    QJsonObject getAppConfigFeaturedProviders();
+
+    QJsonObject getUsrConfigApplication();
+    bool setUsrConfigApplication(const QJsonObject &object);
+    QJsonObject getUsrConfigProviders();
+    bool setUsrConfigProviders(const QJsonObject &object);
+    QJsonObject getUsrConfigCategories();
+    bool setUsrConfigCategories(const QJsonObject &object);
+    QJsonObject getUsrConfigInstalledItems();
+    bool setUsrConfigInstalledItems(const QJsonObject &object);
+
+    bool setUsrConfigProvidersProvider(const QString &providerKey, const QJsonObject &object);
+    bool removeUsrConfigProvidersProvider(const QString &providerKey);
+    bool setUsrConfigCategoriesProvider(const QString &providerKey, const QJsonObject &object);
+    bool removeUsrConfigCategoriesProvider(const QString &providerKey);
+    bool setUsrConfigCategoriesInstallType(const QString &providerKey, const QString &categoryKey, const QString &installType);
+    bool setUsrConfigInstalledItemsItem(const QString &itemKey, const QJsonObject &object);
+    bool removeUsrConfigInstalledItemsItem(const QString &itemKey);
+
+private:
+    QString convertPathString(const QString &path);
+
+    qtlib::Config appConfig_;
+    qtlib::Config usrConfig_;
+    QJsonObject appConfigApplication_;
+    QJsonObject appConfigInstallTypes_;
+};
diff --git a/app/src/handlers/itemhandler.cpp b/app/src/handlers/itemhandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eb11a85d3665072ae5e888e13a98fe7da9a7806c
--- /dev/null
+++ b/app/src/handlers/itemhandler.cpp
@@ -0,0 +1,308 @@
+#include "itemhandler.h"
+
+#include <QJsonValue>
+#include <QJsonArray>
+#include <QFileInfo>
+#include <QDateTime>
+
+#ifdef QTLIB_UNIX
+#include <QProcess>
+#endif
+
+#include "qtlib_dir.h"
+#include "qtlib_file.h"
+#include "qtlib_networkresource.h"
+#include "qtlib_package.h"
+
+#include "handlers/confighandler.h"
+
+ItemHandler::ItemHandler(ConfigHandler *configHandler, QObject *parent)
+    : QObject(parent), configHandler_(configHandler)
+{}
+
+QJsonObject ItemHandler::metadataSet() const
+{
+    return metadataSet_;
+}
+
+void ItemHandler::download(const QString &url, const QString &installType, const QString &providerKey, const QString &contentId)
+{
+    // Use URL as unique key for metadata, network resource, and installed item
+    QString itemKey = url;
+
+    QJsonObject metadata;
+    metadata["url"] = url;
+    metadata["filename"] = QUrl(url).fileName();
+    metadata["install_type"] = installType;
+    metadata["provider"] = providerKey;
+    metadata["content_id"] = contentId;
+    metadata["files"] = QJsonArray();
+    metadata["installed_at"] = qint64();
+
+    QJsonObject result;
+    result["metadata"] = metadata;
+
+    QJsonObject itemMetadataSet = metadataSet();
+
+    if (itemMetadataSet.contains(itemKey)) {
+        result["status"] = QString("error_downloadstart");
+        result["message"] = tr("The file already downloading");
+        emit downloadStarted(result);
+        return;
+    }
+
+    itemMetadataSet[itemKey] = metadata;
+    setMetadataSet(itemMetadataSet);
+
+    qtlib::NetworkResource *resource = new qtlib::NetworkResource(itemKey, QUrl(url), true, this);
+    connect(resource, &qtlib::NetworkResource::downloadProgress, this, &ItemHandler::downloadProgress);
+    connect(resource, &qtlib::NetworkResource::finished, this, &ItemHandler::networkResourceFinished);
+    resource->get();
+
+    result["status"] = QString("success_downloadstart");
+    result["message"] = tr("Downloading");
+    emit downloadStarted(result);
+}
+
+void ItemHandler::uninstall(const QString &itemKey)
+{
+    QJsonObject result;
+    result["status"] = QString("success_uninstallstart");
+    result["message"] = tr("Uninstalling");
+    emit uninstallStarted(result);
+
+    QJsonObject installedItem = configHandler_->getUsrConfigInstalledItems()[itemKey].toObject();
+    QString installType = installedItem["install_type"].toString();
+
+    qtlib::Dir destDir;
+#ifdef QTLIB_UNIX
+    destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["destination"].toString());
+
+    foreach (const QJsonValue &filename, installedItem["files"].toArray()) {
+        QFileInfo fileInfo(destDir.path() + "/" + filename.toString());
+
+        // plasmapkg: Installation process has should be saved plasmapkg into destination directory
+
+        qtlib::Package package(fileInfo.filePath());
+
+        // Uninstall
+        if (installType == "bin") {
+            if (fileInfo.filePath().endsWith(".appimage", Qt::CaseInsensitive)) {
+                QProcess process;
+                process.start(fileInfo.filePath() + " --remove-appimage-desktop-integration");
+                process.waitForFinished();
+            }
+        }
+        else if (installType == "plasma_plasmoids" || installType == "plasma4_plasmoids" || installType == "plasma5_plasmoids") {
+            package.uninstallAsPlasmapkg("plasmoid");
+        }
+        else if (installType == "plasma_look_and_feel" || installType == "plasma5_look_and_feel") {
+            package.uninstallAsPlasmapkg("lookandfeel");
+        }
+        else if (installType == "plasma_desktopthemes" || installType == "plasma5_desktopthemes") {
+            package.uninstallAsPlasmapkg("theme");
+        }
+        else if (installType == "kwin_effects") {
+            package.uninstallAsPlasmapkg("kwineffect");
+        }
+        else if (installType == "kwin_scripts") {
+            package.uninstallAsPlasmapkg("kwinscript");
+        }
+        else if (installType == "kwin_tabbox") {
+            package.uninstallAsPlasmapkg("windowswitcher");
+        }
+
+        // Remove file
+        if (fileInfo.isDir()) {
+            qtlib::Dir(fileInfo.filePath()).remove();
+        }
+        else {
+            qtlib::File(fileInfo.filePath()).remove();
+        }
+    }
+#else
+    destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["generic_destination"].toString());
+
+    foreach (const QJsonValue &filename, installedItem["files"].toArray()) {
+        QFileInfo fileInfo(destDir.path() + "/" + filename.toString());
+        if (fileInfo.isDir()) {
+            qtlib::Dir(fileInfo.filePath()).remove();
+        }
+        else {
+            qtlib::File(fileInfo.filePath()).remove();
+        }
+    }
+#endif
+
+    configHandler_->removeUsrConfigInstalledItemsItem(itemKey);
+
+    result["status"] = QString("success_uninstall");
+    result["message"] = tr("Uninstalled");
+    emit uninstallFinished(result);
+}
+
+void ItemHandler::networkResourceFinished(qtlib::NetworkResource *resource)
+{
+    QString itemKey = resource->id();
+
+    QJsonObject itemMetadataSet = metadataSet();
+    QJsonObject metadata = itemMetadataSet[itemKey].toObject();
+
+    QJsonObject result;
+    result["metadata"] = metadata;
+
+    if (!resource->isFinishedWithNoError()) {
+        itemMetadataSet.remove(itemKey);
+        setMetadataSet(itemMetadataSet);
+        result["status"] = QString("error_download");
+        result["message"] = resource->reply()->errorString();
+        emit downloadFinished(result);
+        resource->deleteLater();
+        return;
+    }
+
+    result["status"] = QString("success_download");
+    result["message"] = tr("Downloaded");
+    emit downloadFinished(result);
+
+    install(resource);
+}
+
+void ItemHandler::setMetadataSet(const QJsonObject &metadataSet)
+{
+    metadataSet_ = metadataSet;
+    emit metadataSetChanged();
+}
+
+void ItemHandler::install(qtlib::NetworkResource *resource)
+{
+    // Installation pre-process
+    QString itemKey = resource->id();
+
+    QJsonObject itemMetadataSet = metadataSet();
+    QJsonObject metadata = itemMetadataSet[itemKey].toObject();
+
+    itemMetadataSet.remove(itemKey);
+    setMetadataSet(itemMetadataSet);
+
+    QJsonObject result;
+    result["metadata"] = metadata;
+    result["status"] = QString("success_installstart");
+    result["message"] = tr("Installing");
+    emit installStarted(result);
+
+    QString filename = metadata["filename"].toString();
+    QString installType = metadata["install_type"].toString();
+
+    QString prefix = configHandler_->getAppConfigApplication()["id"].toString() + "_" + filename;
+    qtlib::Dir tempDir(qtlib::Dir::tempPath() + "/" + prefix);
+    tempDir.make();
+    qtlib::Dir tempDestDir(tempDir.path() + "/dest");
+    tempDestDir.make();
+    qtlib::Package package(tempDir.path() + "/" + filename);
+
+    if (!resource->saveData(package.path())) {
+        result["status"] = QString("error_install");
+        result["message"] = tr("Failed to save data");
+        emit installFinished(result);
+        tempDir.remove();
+        resource->deleteLater();
+        return;
+    }
+
+    // Installation main-process
+    qtlib::Dir destDir;
+#ifdef QTLIB_UNIX
+    destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["destination"].toString());
+
+    // plasmapkg: Need to save package to remove installed files later
+
+    if (installType == "bin"
+            && package.installAsProgram(tempDestDir.path() + "/" + filename)) {
+        result["message"] = tr("The file has been installed as program");
+    }
+    else if ((installType == "plasma_plasmoids" || installType == "plasma4_plasmoids" || installType == "plasma5_plasmoids")
+             && package.installAsPlasmapkg("plasmoid")) {
+        package.installAsFile(tempDestDir.path() + "/" + filename);
+        result["message"] = tr("The plasmoid has been installed");
+    }
+    else if ((installType == "plasma_look_and_feel" || installType == "plasma5_look_and_feel")
+             && package.installAsPlasmapkg("lookandfeel")) {
+        package.installAsFile(tempDestDir.path() + "/" + filename);
+        result["message"] = tr("The plasma look and feel has been installed");
+    }
+    else if ((installType == "plasma_desktopthemes" || installType == "plasma5_desktopthemes")
+             && package.installAsPlasmapkg("theme")) {
+        package.installAsFile(tempDestDir.path() + "/" + filename);
+        result["message"] = tr("The plasma desktop theme has been installed");
+    }
+    else if (installType == "kwin_effects"
+             && package.installAsPlasmapkg("kwineffect")) {
+        package.installAsFile(tempDestDir.path() + "/" + filename);
+        result["message"] = tr("The KWin effect has been installed");
+    }
+    else if (installType == "kwin_scripts"
+             && package.installAsPlasmapkg("kwinscript")) {
+        package.installAsFile(tempDestDir.path() + "/" + filename);
+        result["message"] = tr("The KWin script has been installed");
+    }
+    else if (installType == "kwin_tabbox"
+             && package.installAsPlasmapkg("windowswitcher")) {
+        package.installAsFile(tempDestDir.path() + "/" + filename);
+        result["message"] = tr("The KWin window switcher has been installed");
+    }
+    else if (package.installAsArchive(tempDestDir.path())) {
+        result["message"] = tr("The archive file has been extracted");
+    }
+    else if (package.installAsFile(tempDestDir.path() + "/" + filename)) {
+        result["message"] = tr("The file has been installed");
+    }
+    else {
+        result["status"] = QString("error_install");
+        result["message"] = tr("Failed to installation");
+        emit installFinished(result);
+        tempDir.remove();
+        resource->deleteLater();
+        return;
+    }
+#else
+    destDir.setPath(configHandler_->getAppConfigInstallTypes()[installType].toObject()["generic_destination"].toString());
+
+    if (qtlib::File(package.path()).copy(tempDestDir.path() + "/" + filename)) {
+        result["message"] = tr("The file has been installed");
+    }
+    else {
+        result["status"] = QString("error_install");
+        result["message"] = tr("Failed to installation");
+        emit installFinished(result);
+        tempDir.remove();
+        resource->deleteLater();
+        return;
+    }
+#endif
+
+    destDir.make();
+
+    QJsonArray installedFiles;
+    foreach (const QFileInfo &fileInfo, tempDestDir.list()) {
+        installedFiles.append(QJsonValue(fileInfo.fileName()));
+        if (fileInfo.isDir()) {
+            qtlib::Dir(fileInfo.filePath()).move(destDir.path() + "/" + fileInfo.fileName());
+        }
+        else {
+            qtlib::File(fileInfo.filePath()).move(destDir.path() + "/" + fileInfo.fileName());
+        }
+    }
+
+    // Installation post-process
+    metadata["files"] = installedFiles;
+    metadata["installed_at"] = QDateTime::currentMSecsSinceEpoch();
+    configHandler_->setUsrConfigInstalledItemsItem(itemKey, metadata);
+
+    result["metadata"] = metadata;
+    result["status"] = QString("success_install");
+    emit installFinished(result);
+
+    tempDir.remove();
+    resource->deleteLater();
+}
diff --git a/app/src/handlers/itemhandler.h b/app/src/handlers/itemhandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..2bfebf8ec16aeb13b7dcd6e7ba7924c54bc539fc
--- /dev/null
+++ b/app/src/handlers/itemhandler.h
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <QObject>
+#include <QJsonObject>
+
+namespace qtlib {
+class NetworkResource;
+}
+
+class ConfigHandler;
+
+class ItemHandler : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit ItemHandler(ConfigHandler *configHandler, QObject *parent = 0);
+
+signals:
+    void metadataSetChanged();
+    void downloadStarted(QJsonObject result);
+    void downloadFinished(QJsonObject result);
+    void downloadProgress(QString id, qint64 bytesReceived, qint64 bytesTotal);
+    void installStarted(QJsonObject result);
+    void installFinished(QJsonObject result);
+    void uninstallStarted(QJsonObject result);
+    void uninstallFinished(QJsonObject result);
+
+public slots:
+    QJsonObject metadataSet() const;
+
+    void download(const QString &url, const QString &installType, const QString &providerKey, const QString &contentId);
+    void uninstall(const QString &itemKey);
+
+private slots:
+    void networkResourceFinished(qtlib::NetworkResource *resource);
+
+private:
+    void setMetadataSet(const QJsonObject &metadataSet);
+
+    void install(qtlib::NetworkResource *resource);
+
+    ConfigHandler *configHandler_;
+    QJsonObject metadataSet_;
+};
diff --git a/app/src/handlers/ocshandler.cpp b/app/src/handlers/ocshandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d82a900705c51128149039ec1555ecaa65534825
--- /dev/null
+++ b/app/src/handlers/ocshandler.cpp
@@ -0,0 +1,203 @@
+#include "ocshandler.h"
+
+#include <QJsonValue>
+
+#include "qtlib_ocsapi.h"
+
+#include "handlers/confighandler.h"
+
+OcsHandler::OcsHandler(ConfigHandler *configHandler, QObject *parent)
+    : QObject(parent), configHandler_(configHandler)
+{}
+
+bool OcsHandler::addProviders(const QString &providerFileUrl)
+{
+    QJsonArray providers = qtlib::OcsApi::getProviderFile(QUrl(providerFileUrl));
+    if (!providers.isEmpty()) {
+        foreach (const QJsonValue &providerValue, providers) {
+            QJsonObject provider = providerValue.toObject();
+            if (provider.contains("location")) {
+                // Use location (API base URL) as unique key
+                QString providerKey = provider["location"].toString();
+                if (configHandler_->setUsrConfigProvidersProvider(providerKey, provider)) {
+                    updateCategories(providerKey, true);
+                }
+            }
+        }
+        return true;
+    }
+    return false;
+}
+
+bool OcsHandler::removeProvider(const QString &providerKey)
+{
+    if (configHandler_->removeUsrConfigProvidersProvider(providerKey)) {
+        configHandler_->removeUsrConfigCategoriesProvider(providerKey);
+        return true;
+    }
+    return false;
+}
+
+bool OcsHandler::updateAllCategories(bool force)
+{
+    QJsonObject providers = configHandler_->getUsrConfigProviders();
+    if (!providers.isEmpty()) {
+        foreach (const QString &providerKey, providers.keys()) {
+            updateCategories(providerKey, force);
+        }
+        return true;
+    }
+    return false;
+}
+
+bool OcsHandler::updateCategories(const QString &providerKey, bool force)
+{
+    QJsonObject providers = configHandler_->getUsrConfigProviders();
+
+    if (!providers.contains(providerKey)) {
+        return false;
+    }
+
+    QString baseUrl = providers[providerKey].toObject()["location"].toString();
+    QJsonObject response = qtlib::OcsApi(baseUrl, QUrl(baseUrl)).getContentCategories();
+
+    if (!response.contains("data")) {
+        return false;
+    }
+
+    // Data type variation workaround, convert object to array
+    QJsonArray responseData;
+    if (response["data"].isObject()) {
+        foreach (const QJsonValue &dataValue, response["data"].toObject()) {
+            responseData.append(dataValue);
+        }
+    }
+    else {
+        responseData = response["data"].toArray();
+    }
+
+    QJsonObject installTypes = configHandler_->getAppConfigInstallTypes();
+
+    QJsonObject categories = configHandler_->getUsrConfigCategories();
+    QJsonObject providerCategories;
+    if (!force && categories.contains(providerKey)) {
+        providerCategories = categories[providerKey].toObject();
+    }
+
+    QJsonObject newProviderCategories;
+    foreach (const QJsonValue &dataValue, responseData) {
+        QJsonObject data = dataValue.toObject();
+
+        // Data type variation workaround, convert int to string
+        QString id;
+        if (data["id"].isString()) {
+            id = data["id"].toString();
+        }
+        else {
+            id = QString::number(data["id"].toInt());
+        }
+
+        // Use category id as unique key
+        QString categoryKey = id;
+
+        QString name = data["name"].toString();
+        // display_name: Not compatible to legacy OCS-API
+        if (data.contains("display_name") && data["display_name"].toString() != "") {
+            name = data["display_name"].toString();
+        }
+
+        // parent_id: Not compatible to legacy OCS-API
+        QString parentId = "";
+        if (data.contains("parent_id") && data["parent_id"].toString() != "") {
+            parentId = data["parent_id"].toString();
+        }
+
+        QString installType = configHandler_->getAppConfigApplication()["options"].toObject()["default_install_type"].toString();
+        if (!force && providerCategories.contains(categoryKey)) {
+            installType = providerCategories[categoryKey].toObject()["install_type"].toString();
+        }
+        // xdg_type: Not compatible to legacy OCS-API
+        else if (data.contains("xdg_type") && data["xdg_type"].toString() != ""
+                && installTypes.contains(data["xdg_type"].toString())) {
+            installType = data["xdg_type"].toString();
+        }
+
+        QJsonObject category;
+        category["id"] = id;
+        category["name"] = name;
+        category["parent_id"] = parentId;
+        category["install_type"] = installType;
+        newProviderCategories[categoryKey] = category;
+    }
+
+    return configHandler_->setUsrConfigCategoriesProvider(providerKey, newProviderCategories);
+}
+
+QJsonObject OcsHandler::getContents(const QString &providerKeys, const QString &categoryKeys,
+                                    const QString &xdgTypes, const QString &packageTypes,
+                                    const QString &search, const QString &sortmode, int pagesize, int page)
+{
+    QJsonObject responseSet;
+
+    QStringList providerKeyList;
+    if (!providerKeys.isEmpty()) {
+        providerKeyList = providerKeys.split(",");
+    }
+
+    QStringList categoryKeyList;
+    if (!categoryKeys.isEmpty()) {
+        categoryKeyList = categoryKeys.split(",");
+    }
+
+    QJsonObject providers = configHandler_->getUsrConfigProviders();
+    QJsonObject categories = configHandler_->getUsrConfigCategories();
+
+    foreach (const QString &providerKey, providers.keys()) {
+        if (!providerKeyList.isEmpty() && !providerKeyList.contains(providerKey)) {
+            continue;
+        }
+        QStringList categoryIdList;
+        QJsonObject providerCategories = categories[providerKey].toObject();
+        foreach (const QString &categoryKey, providerCategories.keys()) {
+            if (!categoryKeyList.isEmpty() && !categoryKeyList.contains(categoryKey)) {
+                continue;
+            }
+            categoryIdList.append(providerCategories[categoryKey].toObject()["id"].toString());
+        }
+        if (!categoryIdList.isEmpty()) {
+            QString baseUrl = providers[providerKey].toObject()["location"].toString();
+            QUrlQuery query;
+            // categories: Comma-separated list is not compatible to legacy OCS-API
+            //query.addQueryItem("categories", categoryIdList.join(","));
+            query.addQueryItem("categories", categoryIdList.join("x"));
+            // xdg_types: Not compatible to legacy OCS-API
+            if (!xdgTypes.isEmpty()) {
+                query.addQueryItem("xdg_types", xdgTypes);
+            }
+            // package_types: Not compatible to legacy OCS-API
+            if (!packageTypes.isEmpty()) {
+                query.addQueryItem("package_types", packageTypes);
+            }
+            if (!search.isEmpty()) {
+                query.addQueryItem("search", search);
+            }
+            query.addQueryItem("sortmode", sortmode);
+            query.addQueryItem("pagesize", QString::number(pagesize));
+            query.addQueryItem("page", QString::number(page));
+            responseSet[providerKey] = qtlib::OcsApi(baseUrl, QUrl(baseUrl)).getContentDataSet(query);
+        }
+    }
+
+    return responseSet;
+}
+
+QJsonObject OcsHandler::getContent(const QString &providerKey, const QString &contentId)
+{
+    QJsonObject response;
+    QJsonObject providers = configHandler_->getUsrConfigProviders();
+    if (providers.contains(providerKey)) {
+        QString baseUrl = providers[providerKey].toObject()["location"].toString();
+        response = qtlib::OcsApi(baseUrl, QUrl(baseUrl)).getContentData(contentId);
+    }
+    return response;
+}
diff --git a/app/src/handlers/ocshandler.h b/app/src/handlers/ocshandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..72b4853817297bab6a71309a1384fdebe2b1fbf3
--- /dev/null
+++ b/app/src/handlers/ocshandler.h
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <QObject>
+#include <QJsonObject>
+
+class ConfigHandler;
+
+class OcsHandler : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit OcsHandler(ConfigHandler *configHandler, QObject *parent = 0);
+
+public slots:
+    bool addProviders(const QString &providerFileUrl);
+    bool removeProvider(const QString &providerKey);
+    bool updateAllCategories(bool force = false);
+    bool updateCategories(const QString &providerKey, bool force = false);
+    QJsonObject getContents(const QString &providerKeys = "", const QString &categoryKeys = "",
+                            const QString &xdgTypes = "", const QString &packageTypes = "",
+                            const QString &search = "", const QString &sortmode = "new", int pagesize = 25, int page = 0);
+    QJsonObject getContent(const QString &providerKey, const QString &contentId);
+
+private:
+    ConfigHandler *configHandler_;
+};
diff --git a/app/src/handlers/systemhandler.cpp b/app/src/handlers/systemhandler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b7b95ae612ad117f55182713ee9ec9f7b9211a91
--- /dev/null
+++ b/app/src/handlers/systemhandler.cpp
@@ -0,0 +1,204 @@
+#include "systemhandler.h"
+
+#ifdef QTLIB_UNIX
+#include <QFileInfo>
+#include <QProcess>
+#include <QDBusMessage>
+#include <QDBusConnection>
+#include <QDBusVariant>
+#include <QDebug>
+#endif
+
+SystemHandler::SystemHandler(QObject *parent)
+    : QObject(parent)
+{}
+
+bool SystemHandler::isUnix()
+{
+#ifdef QTLIB_UNIX
+    return true;
+#endif
+    return false;
+}
+
+QString SystemHandler::desktopEnvironment()
+{
+    QString desktop = "unknown";
+    QString currentDesktop = "";
+
+    if (!qgetenv("XDG_CURRENT_DESKTOP").isEmpty()) {
+        currentDesktop = QString::fromLocal8Bit(qgetenv("XDG_CURRENT_DESKTOP").constData()).toLower();
+    }
+    else if (!qgetenv("XDG_SESSION_DESKTOP").isEmpty()) {
+        currentDesktop = QString::fromLocal8Bit(qgetenv("XDG_SESSION_DESKTOP").constData()).toLower();
+    }
+    else if (!qgetenv("DESKTOP_SESSION").isEmpty()) {
+        currentDesktop = QString::fromLocal8Bit(qgetenv("DESKTOP_SESSION").constData()).toLower();
+    }
+
+    if (currentDesktop.contains("kde") || currentDesktop.contains("plasma")) {
+        desktop = "kde";
+    }
+    else if (currentDesktop.contains("gnome") || currentDesktop.contains("unity")) {
+        desktop = "gnome";
+    }
+    else if (currentDesktop.contains("xfce")) {
+        desktop = "xfce";
+    }
+    return desktop;
+}
+
+bool SystemHandler::isApplicableType(const QString &installType)
+{
+    QString desktop = desktopEnvironment();
+
+    if (installType == "wallpapers"
+            && (desktop == "kde" || desktop == "gnome" || desktop == "xfce")) {
+        return true;
+    }
+    /*else if (installType == "icons"
+             && (desktop == "kde" || desktop == "gnome" || desktop == "xfce")) {
+        return true;
+    }
+    else if (installType == "cursors"
+             && (desktop == "kde" || desktop == "gnome" || desktop == "xfce")) {
+        return true;
+    }
+    else if ((installType == "aurorae_themes" && desktop == "kde")
+             || (installType == "metacity_themes" && desktop == "gnome")
+             || (installType == "xfwm4_themes" && desktop == "xfce")) {
+        return true;
+    }*/
+    return false;
+}
+
+#ifdef QTLIB_UNIX
+bool SystemHandler::applyFile(const QString &path, const QString &installType)
+{
+    if (QFileInfo::exists(path) && isApplicableType(installType)) {
+        if (installType == "wallpapers") {
+            return applyWallpaper(path);
+        }
+        /*else if (installType == "icons") {
+            return applyIcon(path);
+        }
+        else if (installType == "cursors") {
+            return applyCursor(path);
+        }
+        else if (installType == "aurorae_themes"
+                 || installType == "metacity_themes"
+                 || installType == "xfwm4_themes") {
+            return applyWindowTheme(path);
+        }*/
+    }
+    return false;
+}
+#endif
+
+#ifdef QTLIB_UNIX
+bool SystemHandler::applyWallpaper(const QString &path)
+{
+    QString desktop = desktopEnvironment();
+
+    if (desktop == "kde") {
+        // plasma5.6+
+        QDBusMessage message = QDBusMessage::createMethodCall("org.kde.plasmashell", "/PlasmaShell", "org.kde.PlasmaShell", "evaluateScript");
+        QVariantList arguments;
+
+        QString script;
+        QTextStream out(&script);
+        out << "for (var key in desktops()) {"
+            << "var d = desktops()[key];"
+            << "d.wallpaperPlugin = 'org.kde.image';"
+            << "d.currentConfigGroup = ['Wallpaper', 'org.kde.image', 'General'];"
+            << "d.writeConfig('Image', 'file://" + path + "');"
+            << "}";
+
+        arguments << QVariant(script);
+        message.setArguments(arguments);
+
+        QDBusMessage reply = QDBusConnection::sessionBus().call(message);
+
+        if (reply.type() == QDBusMessage::ErrorMessage) {
+            qWarning() << reply.errorMessage();
+            return false;
+        }
+        return true;
+    }
+    else if (desktop == "gnome") {
+        QStringList arguments;
+        // gnome3
+        arguments << "set" << "org.gnome.desktop.background" << "picture-uri" << "file://" + path;
+        return QProcess::startDetached("gsettings", arguments);
+        // gnome2
+        //arguments << "--type=string" << "--set" << "/desktop/gnome/background/picture_filename" << path;
+        //return QProcess::startDetached("gconftool-2", arguments);
+    }
+    else if (desktop == "xfce") {
+        QDBusMessage message = QDBusMessage::createMethodCall("org.xfce.Xfconf", "/org/xfce/Xfconf", "org.xfce.Xfconf", "SetProperty");
+        QVariantList arguments;
+
+        QString channelValue = "xfce4-desktop";
+        //QString propertyValue = "/backdrop/screen0/monitor0/image-path";
+        QString propertyValue = "/backdrop/screen0/monitor0/workspace0/last-image";
+        QDBusVariant valueValue(path);
+
+        arguments << QVariant(channelValue) << QVariant(propertyValue) << QVariant::fromValue(valueValue);
+        message.setArguments(arguments);
+
+        QDBusMessage reply = QDBusConnection::sessionBus().call(message);
+
+        if (reply.type() == QDBusMessage::ErrorMessage) {
+            qWarning() << reply.errorMessage();
+            return false;
+        }
+        return true;
+    }
+    return false;
+}
+
+bool SystemHandler::applyIcon(const QString &path)
+{
+    qDebug() << path;
+
+    QString desktop = desktopEnvironment();
+
+    if (desktop == "kde") {
+    }
+    else if (desktop == "gnome") {
+    }
+    else if (desktop == "xfce") {
+    }
+    return false;
+}
+
+bool SystemHandler::applyCursor(const QString &path)
+{
+    qDebug() << path;
+
+    QString desktop = desktopEnvironment();
+
+    if (desktop == "kde") {
+    }
+    else if (desktop == "gnome") {
+    }
+    else if (desktop == "xfce") {
+    }
+    return false;
+}
+
+bool SystemHandler::applyWindowTheme(const QString &path)
+{
+    qDebug() << path;
+
+    QString desktop = desktopEnvironment();
+
+    if (desktop == "kde") {
+    }
+    else if (desktop == "gnome") {
+    }
+    else if (desktop == "xfce") {
+    }
+    return false;
+}
+#endif
diff --git a/app/src/handlers/systemhandler.h b/app/src/handlers/systemhandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..a49f9a6a6b7a1aff0ee9e9036eea2a40a8e625cd
--- /dev/null
+++ b/app/src/handlers/systemhandler.h
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <QObject>
+
+class SystemHandler : public QObject
+{
+    Q_OBJECT
+
+public:
+    explicit SystemHandler(QObject *parent = 0);
+
+public slots:
+    bool isUnix();
+
+    QString desktopEnvironment();
+    bool isApplicableType(const QString &installType);
+
+#ifdef QTLIB_UNIX
+    bool applyFile(const QString &path, const QString &installType);
+#endif
+
+private:
+#ifdef QTLIB_UNIX
+    bool applyWallpaper(const QString &path);
+    bool applyIcon(const QString &path);
+    bool applyCursor(const QString &path);
+    bool applyWindowTheme(const QString &path);
+#endif
+};
diff --git a/app/src/main.cpp b/app/src/main.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..23fe103dec3d427b52043d5265b2ff7f5a2e50c3
--- /dev/null
+++ b/app/src/main.cpp
@@ -0,0 +1,39 @@
+#include <QJsonObject>
+//#include <QTranslator>
+//#include <QLocale>
+#include <QCommandLineParser>
+#include <QCoreApplication>
+
+#include "handlers/confighandler.h"
+#include "handlers/systemhandler.h"
+#include "handlers/ocshandler.h"
+#include "handlers/itemhandler.h"
+
+int main(int argc, char *argv[])
+{
+    // Init
+    QCoreApplication app(argc, argv);
+
+    ConfigHandler *configHandler = new ConfigHandler();
+    QJsonObject appConfigApplication = configHandler->getAppConfigApplication();
+
+    app.setApplicationName(appConfigApplication["name"].toString());
+    app.setApplicationVersion(appConfigApplication["version"].toString());
+    app.setOrganizationName(appConfigApplication["organization"].toString());
+    app.setOrganizationDomain(appConfigApplication["domain"].toString());
+
+    // Setup translator
+    //QTranslator translator;
+    //if (translator.load(QLocale(), "messages", ".", ":/i18n")) {
+    //    app.installTranslator(&translator);
+    //}
+
+    // Setup CLI
+    QCommandLineParser clParser;
+    clParser.setApplicationDescription(appConfigApplication["description"].toString());
+    clParser.addHelpOption();
+    clParser.addVersionOption();
+    clParser.process(app);
+
+    return app.exec();
+}
diff --git a/lib/lib.pri b/lib/lib.pri
new file mode 100644
index 0000000000000000000000000000000000000000..5343ea67e320f54da6851de3969e590c456d432d
--- /dev/null
+++ b/lib/lib.pri
@@ -0,0 +1 @@
+include($${PWD}/qtlib/qtlib.pri)
diff --git a/ocs-manager.pro b/ocs-manager.pro
new file mode 100644
index 0000000000000000000000000000000000000000..f1df8facfdf0c4aa4a68db2cf30d7e1ae4d708f7
--- /dev/null
+++ b/ocs-manager.pro
@@ -0,0 +1,19 @@
+message("Please execute scripts/import.sh for build dependencies")
+
+TARGET = ocs-manager
+
+TEMPLATE = app
+
+CONFIG += \
+    c++11 \
+    console
+
+CONFIG -= app_bundle
+
+DEFINES += QT_DEPRECATED_WARNINGS
+
+DISTFILES += $${PWD}/README.md
+
+include($${PWD}/lib/lib.pri)
+include($${PWD}/app/app.pri)
+include($${PWD}/scripts/scripts.pri)
diff --git a/scripts/import.sh b/scripts/import.sh
new file mode 100644
index 0000000000000000000000000000000000000000..50992b4f529fc2ae523927a50bae7cfcca4b9d6f
--- /dev/null
+++ b/scripts/import.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+################################################################################
+# This is utility script to import build dependencies
+################################################################################
+
+PROJDIR="$(cd "$(dirname "${0}")/../" && pwd)"
+
+if [ ! -d "${PROJDIR}/lib/qtlib" ]; then
+    git clone https://github.com/akiraohgaki/qtlib.git -b release-0.1.0 --single-branch --depth=1 "${PROJDIR}/lib/qtlib"
+fi
diff --git a/scripts/scripts.pri b/scripts/scripts.pri
new file mode 100644
index 0000000000000000000000000000000000000000..47e7b4bf65cbaf037ad2933caa803bbe7b7582d6
--- /dev/null
+++ b/scripts/scripts.pri
@@ -0,0 +1 @@
+DISTFILES += $${PWD}/import.sh