Blob Blame History Raw
/*
 * Copyright 2014 Red Hat Inc., Durham, North Carolina.
 * All Rights Reserved.
 *
 * 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 3 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/>.
 *
 * Authors:
 *      Martin Preisler <mpreisle@redhat.com>
 */

#include "SaveAsRPMDialog.h"
#include "MainWindow.h"
#include "ScanningSession.h"
#include "ProcessHelpers.h"

#include <QFileDialog>
#include <QPointer>
#include <cassert>

SaveAsRPMDialog::SaveAsRPMDialog(ScanningSession* session, MainWindow* parent):
    QDialog(parent),
    mMainWindow(parent),

    mScanningSession(session)
{
    mUI.setupUi(this);

    QObject::connect(
        this, SIGNAL(finished(int)),
        this, SLOT(slotFinished(int))
    );

    // See https://fedoraproject.org/wiki/Packaging:NamingGuidelines#CommonCharacterSet
    // Furthermore, we do not allow '.' to avoid confusion.
    mUI.packageName->setValidator(new QRegExpValidator(QRegExp("^[a-zA-Z0-9\\-_\\+]+$"), this));

    const QFileInfo openedFile(mScanningSession->getOpenedFilePath());
    mUI.packageName->setText(openedFile.baseName());

    mUI.version->setValidator(new QRegExpValidator(QRegExp("^([0-9]+\\.)*[0-9]+$"), this));

    show();
}

SaveAsRPMDialog::~SaveAsRPMDialog()
{}

void SaveAsRPMDialog::saveSession(ScanningSession* session, MainWindow* parent)
{
    QPointer<SaveAsRPMDialog> dialog = new SaveAsRPMDialog(session, parent);
    dialog->exec();
    delete dialog;
}

void SaveAsRPMDialog::slotFinished(int result)
{
    if (result == QDialog::Rejected)
        return;

    const QString targetDir = QFileDialog::getExistingDirectory(
        this, QObject::tr("Select target directory"),
        mMainWindow->getDefaultSaveDirectory()
    );

    if (targetDir.isEmpty())
        return; // user canceled

    mMainWindow->notifySaveActionConfirmed(targetDir, true);

    QSet<QString> closure = mScanningSession->getOpenedFilesClosure();
    // At this point, closure is a set which is implementation ordered.
    // (we have no control WRT the ordering)
    // We want to make the XCCDF/SDS/main file appear first because that's
    // what the 'save as RPM' script will use to deduce the package name
    closure.remove(mScanningSession->getOpenedFilePath());
    QList<QString> closureOrdered;
    closureOrdered.append(mScanningSession->getOpenedFilePath());
    closureOrdered.append(closure.toList());

    const QDir cwd = ScanningSession::getCommonAncestorDirectory(closure);

    SyncProcess scapAsRPM(this);
    scapAsRPM.setCommand(SCAP_WORKBENCH_LOCAL_SCAP_AS_RPM_PATH);
    scapAsRPM.setWorkingDirectory(cwd.absolutePath());

    QStringList args;
    if (!mUI.packageName->text().isEmpty())
    {
        args.append("--pkg-name");
        args.append(mUI.packageName->text());
    }
    if (!mUI.version->text().isEmpty())
    {
        args.append("--pkg-version");
        args.append(mUI.version->text());
    }
    // release is a spinbox, it can't be empty
    args.append("--pkg-release"); args.append(mUI.release->text());

    // summary may contain whitespaces, we need a string that has at least one non-whitespace char
    if (!mUI.summary->text().trimmed().isEmpty())
    {
        args.append("--pkg-summary");
        args.append(mUI.summary->text());
    }
    if (!mUI.license->currentText().isEmpty())
    {
        args.append("--pkg-license");
        args.append(mUI.license->currentText());
    }

    args.append("--rpm-destination"); args.append(targetDir);

    for (QList<QString>::const_iterator it = closureOrdered.begin(); it != closureOrdered.end(); ++it)
    {
        args.append(cwd.relativeFilePath(*it));
    }

    SpacelessQTemporaryDir tailoringDir;

    // Tailoring file is a special case since it may be in memory only.
    // In case it is memory only we don't want it to cause our common ancestor dir to be /
    // We export it to a temporary directory and remove it after including it in the RPM
    if (mScanningSession->hasTailoring())
    {
        QFileInfo tailoringFile(mScanningSession->getTailoringFilePath());
        assert(tailoringFile.exists());

        const QString tailoringFilePath = QString("%1/%2").arg(tailoringDir.path(), "tailoring-xccdf.xml");

        ScanningSession::copyOrReplace(tailoringFile.absoluteFilePath(),
            tailoringFilePath);

        args.append(tailoringFilePath);
    }

    scapAsRPM.setArguments(args);

    QPointer<QDialog> dialog = scapAsRPM.runWithDialog(this, QObject::tr("Saving SCAP content as RPM..."));
    dialog->exec();
    delete dialog;
}