/*
    Copyright 2013 by Reza Fatahilah Shah <rshah0385@kireihana.com>
    Copyright 2011, 2012 David Edmundson <kde@davidedmundson.co.uk>
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
   
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
   
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include "sddmauthhelper.h"

#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QSharedPointer>
#include <QDebug>
#include <QMimeType>
#include <QMimeDatabase>

#include <KLocalizedString>
#include <KZip>
#include <KTar>
#include <KArchive>
#include <KConfig>
#include <KConfigGroup>

static QSharedPointer<KConfig> openConfig(const QString &filePath)
{
    QFile file(filePath);
    if(!file.exists()) {
        // If we are creating the config file, ensure it is world-readable: if
        // we don't do that, KConfig will create a file which is only readable
        // by root
        file.open(QIODevice::WriteOnly);
        file.close();
        file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther);
    }
    return QSharedPointer<KConfig>(new KConfig(file.fileName(), KConfig::SimpleConfig));
}

ActionReply SddmAuthHelper::save(const QVariantMap &args)
{
    ActionReply reply = ActionReply::HelperErrorReply();
    QSharedPointer<KConfig> sddmConfig = openConfig(args[QLatin1String("sddm.conf")].toString());
    QSharedPointer<KConfig> themeConfig;
    QString themeConfigFile = args[QLatin1String("theme.conf.user")].toString();

    if (!themeConfigFile.isEmpty()) {
        themeConfig = openConfig(themeConfigFile);
    }

    QMap<QString, QVariant>::const_iterator iterator;
    
    for (iterator = args.constBegin() ; iterator != args.constEnd() ; ++iterator) {
        if (iterator.key() == QLatin1String("sddm.conf") || iterator.key() == QLatin1String("theme.conf.user"))
            continue;

        QStringList configFields = iterator.key().split(QLatin1Char('/'));
        if (configFields.size() != 3) {
            continue;
        }

        QSharedPointer<KConfig> config;
        QString fileName = configFields[0];
        QString groupName = configFields[1];
        QString keyName = configFields[2];

        if (fileName == QLatin1String("sddm.conf")) {
            sddmConfig->group(groupName).writeEntry(keyName, iterator.value());
        } else if (fileName == QLatin1String("theme.conf.user") && !themeConfig.isNull()) {
            QFileInfo themeConfigFileInfo(themeConfigFile);
            QDir configRootDirectory = themeConfigFileInfo.absoluteDir();

            if (keyName == QLatin1String("background")) {
                QFileInfo newBackgroundFileInfo(iterator.value().toString());
                QString previousBackground = themeConfig->group(groupName).readEntry(keyName);

                bool backgroundChanged = newBackgroundFileInfo.fileName() != previousBackground;
                if (backgroundChanged) {
                    if (!previousBackground.isEmpty()) {
                        QString previousBackgroundPath = configRootDirectory.filePath(previousBackground);
                        if (QFile::remove(previousBackgroundPath)) {
                            qDebug() << "Removed previous background " << previousBackgroundPath;
                        }
                    }

                    if (newBackgroundFileInfo.exists()) {
                        QString newBackgroundPath = configRootDirectory.filePath(newBackgroundFileInfo.fileName());
                        qDebug() << "Copying background from "  << newBackgroundFileInfo.absoluteFilePath() << " to " << newBackgroundPath;
                        if (QFile::copy(newBackgroundFileInfo.absoluteFilePath(), newBackgroundPath)) {
                            QFile::setPermissions(newBackgroundPath, QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther);
                            themeConfig->group(groupName).writeEntry(keyName, newBackgroundFileInfo.fileName());
                        }
                    } else {
                        themeConfig->group(groupName).deleteEntry(keyName);
                    }
                }
            } else {
                themeConfig->group(groupName).writeEntry(keyName, iterator.value());
            }
        }
    }

    sddmConfig->sync();

    if (!themeConfig.isNull())
        themeConfig->sync();
    
    return ActionReply::SuccessReply();
}

ActionReply SddmAuthHelper::installtheme(const QVariantMap &args)
{
    const QString filePath = args[QStringLiteral("filePath")].toString();
    if (filePath.isEmpty()) {
        return ActionReply::HelperErrorReply();
    }

    const QString themesBaseDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sddm/themes"), QStandardPaths::LocateDirectory);
    QDir dir(themesBaseDir);
    if (!dir.exists()) {
        return ActionReply::HelperErrorReply();
    }

    qDebug() << "Installing " << filePath << " into " << themesBaseDir;

    if (!QFile::exists(filePath)) {
        return ActionReply::HelperErrorReply();
    }

    QMimeDatabase db;
    QMimeType mimeType = db.mimeTypeForFile(filePath);
    qWarning() << "Postinstallation: uncompress the file";

    QScopedPointer<KArchive> archive;

    //there must be a better way to do this? If not, make a static bool KZip::supportsMimeType(const QMimeType &type); ?
    //or even a factory class in KArchive

    if (mimeType.inherits(QStringLiteral("application/zip"))) {
        archive.reset(new KZip(filePath));
    } else if (mimeType.inherits(QStringLiteral("application/tar"))
                || mimeType.inherits(QStringLiteral("application/x-gzip"))
                || mimeType.inherits(QStringLiteral("application/x-bzip"))
                || mimeType.inherits(QStringLiteral("application/x-lzma"))
                || mimeType.inherits(QStringLiteral("application/x-xz"))
                || mimeType.inherits(QStringLiteral("application/x-bzip-compressed-tar"))
                || mimeType.inherits(QStringLiteral("application/x-compressed-tar"))) {
        archive.reset(new KTar(filePath));
    } else {
        auto e = ActionReply::HelperErrorReply();
        e.setErrorDescription(i18n("Invalid theme package"));
        return e;    }

    if (!archive->open(QIODevice::ReadOnly)) {
        auto e = ActionReply::HelperErrorReply();
        e.setErrorDescription(i18n("Could not open file"));
        return e;
    }

    auto directory = archive->directory();

    QStringList installedPaths;

    //some basic validation
    //the top level should only have folders, and those folders should contain a valid metadata.desktop file
    //if we get anything else, abort everything before copying
    for(const QString &name: directory->entries()) {
        auto entry = directory->entry(name);
        if (!entry->isDirectory()) {
            auto e = ActionReply::HelperErrorReply();
            e.setErrorDescription(i18n("Invalid theme package"));
            return e;
        }
        auto subDirectory = static_cast<const KArchiveDirectory*>(entry);
        auto metadataFile = subDirectory->file(QStringLiteral("metadata.desktop"));
        if(!metadataFile || !metadataFile->data().contains("[SddmGreeterTheme]")) {
            auto e = ActionReply::HelperErrorReply();
            e.setErrorDescription(i18n("Invalid theme package"));
            return e;
        }
        installedPaths.append(themesBaseDir + QLatin1Char('/') + name);
    }

    if (!directory->copyTo(themesBaseDir)) {
        auto e = ActionReply::HelperErrorReply();
        e.setErrorDescription(i18n("Could not decompress archive"));
        return e;
    }

    auto rc = ActionReply::SuccessReply();
    rc.addData(QStringLiteral("installedPaths"), installedPaths);
    return rc;
}

ActionReply SddmAuthHelper::uninstalltheme(const QVariantMap &args)
{
    const QString themePath = args[QStringLiteral("filePath")].toString();
    const QString themesBaseDir = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("sddm/themes"), QStandardPaths::LocateDirectory);

    QDir dir(themePath);
    if (!dir.exists()) {
        return ActionReply::HelperErrorReply();
    }

    //validate the themePath is directly inside the themesBaseDir
    QDir baseDir(themesBaseDir);
    if(baseDir.absoluteFilePath(dir.dirName()) != dir.absolutePath()) {
        return ActionReply::HelperErrorReply();
    }

    if (!dir.removeRecursively()) {
        return ActionReply::HelperErrorReply();
    }

    return ActionReply::SuccessReply();
}


KAUTH_HELPER_MAIN("org.kde.kcontrol.kcmsddm", SddmAuthHelper);

#include "moc_sddmauthhelper.cpp"

