QStringList OscapScannerRemoteSsh::getCommandLineArgs() const
{
    QStringList args("oscap-ssh");
    args.append(mSshConnection.getTarget());
    args.append(QString::number(mSshConnection.getPort()));

    if (mScannerMode == SM_OFFLINE_REMEDIATION)
    {
        QTemporaryFile inputARFFile;
        inputARFFile.setAutoRemove(true);
        inputARFFile.open();
        inputARFFile.write(getARFForRemediation());
        inputARFFile.close();

        args += buildOfflineRemediationArgs(inputARFFile.fileName(),
            "/tmp/xccdf-results.xml",
            "/tmp/report.html",
            "/tmp/arf.xml",
            // ignore capabilities because of dry-run
            true
        );
    }
    else
    {
        args += buildEvaluationArgs(mSession->getOpenedFilePath(),
            mSession->getUserTailoringFilePath(),
            "/tmp/xccdf-results.xml",
            "/tmp/report.html",
            "/tmp/arf.xml",
            mScannerMode == SM_SCAN_ONLINE_REMEDIATION,
            // ignore capabilities because of dry-run
            true
        );
    }

    args.removeOne("--progress");

    return args;
}
QStringList OscapScannerLocal::getCommandLineArgs() const
{
    // TODO: This seems outdated (and it is used only during dry runs)
    QStringList args("oscap");

    if (mScannerMode == SM_OFFLINE_REMEDIATION)
    {
        QTemporaryFile inputARFFile;
        inputARFFile.setAutoRemove(true);
        inputARFFile.open();
        inputARFFile.write(getARFForRemediation());
        inputARFFile.close();

        args += buildOfflineRemediationArgs(inputARFFile.fileName(),
            "/tmp/xccdf-results.xml",
            "/tmp/report.html",
            "/tmp/arf.xml",
            // ignore capabilities because of dry-run
            true
        );
    }
    else
    {
        args += buildEvaluationArgs(mSession->getOpenedFilePath(),
            mSession->getUserTailoringFilePath(),
            "/tmp/xccdf-results.xml",
            "/tmp/report.html",
            "/tmp/arf.xml",
            mScannerMode == SM_SCAN_ONLINE_REMEDIATION,
            // ignore capabilities because of dry-run
            true
        );
    }

    args.removeOne("--progress");

    return args;
}
void OscapScannerLocal::evaluate()
{
    if (mDryRun)
    {
        signalCompletion(mCancelRequested);
        return;
    }

    emit infoMessage(QObject::tr("Querying capabilities..."));
    try
    {
        fillInCapabilities();
    }
    catch (std::exception& e)
    {
        emit errorMessage(e.what());
        return;
    }

    if (!checkPrerequisites())
    {
        mCancelRequested = true;
        signalCompletion(mCancelRequested);
        return;
    }

    // TODO: Error handling!
    // This is mainly for check-engine-results and oval-results, to ensure
    // we get a full report, including info from these files. openscap's XSLT
    // uses info in the check engine results if it can find them.

    QProcess process(this);

    emit infoMessage(QObject::tr("Creating temporary files..."));
    // This is mainly for check-engine-results and oval-results, to ensure
    // we get a full report, including info from these files. openscap's XSLT
    // uses info in the check engine results if it can find them.
    TemporaryDir workingDir;
    process.setWorkingDirectory(workingDir.getPath());

    QStringList args;
    QTemporaryFile inputARFFile;

    QTemporaryFile arfFile;
    arfFile.setAutoRemove(true);
    setFilenameToTempFile(arfFile);

    QTemporaryFile reportFile;
    reportFile.setAutoRemove(true);
    setFilenameToTempFile(reportFile);

    QTemporaryFile resultFile;
    resultFile.setAutoRemove(true);
    setFilenameToTempFile(resultFile);


    if (mScannerMode == SM_OFFLINE_REMEDIATION)
    {
        inputARFFile.open();
        inputARFFile.write(getARFForRemediation());
        inputARFFile.close();

        args = buildOfflineRemediationArgs(inputARFFile.fileName(),
                resultFile.fileName(),
                reportFile.fileName(),
                arfFile.fileName());
    }
    else
    {
        args = buildEvaluationArgs(mSession->getOpenedFilePath(),
                mSession->hasTailoring() ? mSession->getTailoringFilePath() : QString(),
                resultFile.fileName(),
                reportFile.fileName(),
                arfFile.fileName(),
                mScannerMode == SM_SCAN_ONLINE_REMEDIATION);
    }
    QString program = getOscapProgramAndAdaptArgs(args);

    emit infoMessage(QObject::tr("Starting the oscap process..."));
    process.start(program, args);
    process.waitForStarted();

    if (process.state() != QProcess::Running)
    {
        emit errorMessage(QObject::tr("Failed to start local scanning process '%1'. Perhaps the executable was not found?").arg(program));
        mCancelRequested = true;
    }

    unsigned int pollInterval = 100;

    emit infoMessage(QObject::tr("Processing..."));
    while (!process.waitForFinished(pollInterval))
    {
        // read everything new
        readStdOut(process);
        watchStdErr(process);

        // pump the event queue, mainly because the user might want to cancel
        QAbstractEventDispatcher::instance(mScanThread)->processEvents(QEventLoop::AllEvents);

        if (mCancelRequested)
        {
            pollInterval = 1000;
            emit infoMessage(QObject::tr("Cancellation was requested! Terminating scanning..."));
            process.kill();
        }
    }

    if (!mCancelRequested)
    {
        if (process.exitCode() == 1) // error happened
        {
            watchStdErr(process);
            // TODO: pass the diagnostics over
            emit errorMessage(QObject::tr("There was an error during evaluation! Exit code of the 'oscap' process was 1."));
            // mark this run as canceled
            mCancelRequested = true;
        }
        else
        {
            // read everything left over
            readStdOut(process);
            watchStdErr(process);

            emit infoMessage(QObject::tr("The oscap tool has finished. Reading results..."));

            resultFile.open();
            mResults = resultFile.readAll();
            resultFile.close();

            reportFile.open();
            mReport = reportFile.readAll();
            reportFile.close();

            arfFile.open();
            mARF = arfFile.readAll();
            arfFile.close();

            emit infoMessage(QObject::tr("Processing has been finished!"));
        }
    }
    else
    {
        emit infoMessage(QObject::tr("Scanning cancelled!"));
    }

    signalCompletion(mCancelRequested);
}
void OscapScannerRemoteSsh::evaluate()
{
    if (mDryRun)
    {
        signalCompletion(mCancelRequested);
        return;
    }

    ensureConnected();

    if (mCancelRequested)
    {
        signalCompletion(true);
        return;
    }

    {
        SshSyncProcess proc(mSshConnection, this);
        emit infoMessage(QObject::tr("Checking if oscap is available on remote machine..."));

        proc.setCommand(QString("command"));
        proc.setArguments(QStringList() << "-v" << SCAP_WORKBENCH_REMOTE_OSCAP_PATH);
        proc.setCancelRequestSource(&mCancelRequested);
        proc.run();

        if (proc.getExitCode() != 0)
        {
            emit errorMessage(
                QObject::tr("Failed to locate oscap on remote machine. "
                        "Please, check that openscap-scanner is installed on the remote machine.")
            );

            mCancelRequested = true;
            signalCompletion(mCancelRequested);
            return;
        }

        emit infoMessage(QObject::tr("Querying capabilities on remote machine..."));
        proc.setCommand(SCAP_WORKBENCH_REMOTE_OSCAP_PATH);
        proc.setArguments(QStringList("-V"));
        proc.setCancelRequestSource(&mCancelRequested);
        proc.run();

        if (proc.getExitCode() != 0)
        {
            emit errorMessage(
                QObject::tr("Failed to query capabilities of oscap on remote machine.\n"
                        "Diagnostic info:\n%1").arg(proc.getDiagnosticInfo())
            );

            mCancelRequested = true;
            signalCompletion(mCancelRequested);
            return;
        }

        mCapabilities.parse(proc.getStdOutContents());
    }

    if (!checkPrerequisites())
    {
        mCancelRequested = true;
        signalCompletion(mCancelRequested);
        return;
    }

    QStringList baseArgs;
    baseArgs.append("-o"); baseArgs.append(QString("ControlPath=%1").arg(mSshConnection._getMasterSocket()));
    baseArgs.append(mTarget);

    QString diagnosticInfo;

    emit infoMessage(QObject::tr("Copying input data to remote target..."));

    const QString inputFile = copyInputFileOver();
    const QString tailoringFile = mSession->hasTailoring() ?
        copyFileOver(mSession->getTailoringFilePath()) : QString();

    if (mCancelRequested)
    {
        signalCompletion(true);
        return;
    }

    const QString reportFile = createRemoteTemporaryFile();
    const QString resultFile = createRemoteTemporaryFile();
    const QString arfFile = createRemoteTemporaryFile();
    const QString workingDir = createRemoteTemporaryDirectory();

    // TODO: We could be leaking any of the temporary files at this point!
    if (mCancelRequested)
    {
        signalCompletion(true);
        return;
    }

    QStringList args;

    if (mScannerMode == SM_OFFLINE_REMEDIATION)
    {
        args = buildOfflineRemediationArgs(inputFile,
                resultFile,
                reportFile,
                arfFile);
    }
    else
    {
        args = buildEvaluationArgs(inputFile,
                tailoringFile,
                resultFile,
                reportFile,
                arfFile,
                mScannerMode == SM_SCAN_ONLINE_REMEDIATION);
    }

    const QString sshCmd = args.join(" ");

    emit infoMessage(QObject::tr("Starting the remote process..."));

    QProcess process(this);

    process.start(SCAP_WORKBENCH_LOCAL_SSH_PATH, baseArgs + QStringList(QString("cd '%1'; " SCAP_WORKBENCH_REMOTE_OSCAP_PATH " %2").arg(workingDir).arg(sshCmd)));
    process.waitForStarted();

    if (process.state() != QProcess::Running)
    {
        emit errorMessage(QObject::tr("Failed to start ssh. Perhaps the executable was not found?"));
        mCancelRequested = true;
    }

    const unsigned int pollInterval = 100;

    emit infoMessage(QObject::tr("Processing on the remote machine..."));
    while (!process.waitForFinished(pollInterval))
    {
        // read everything new
        readStdOut(process);
        watchStdErr(process);

        // pump the event queue, mainly because the user might want to cancel
        QAbstractEventDispatcher::instance(mScanThread)->processEvents(QEventLoop::AllEvents);

        if (mCancelRequested)
        {
            emit infoMessage(QObject::tr("Cancellation was requested! Terminating..."));
            // TODO: On Windows we have to kill immediately, terminate() posts WM_CLOSE
            //       but oscap doesn't have any event loop running.
            process.terminate();
            break;
        }
    }

    if (mCancelRequested)
    {
        unsigned int waited = 0;
        while (!process.waitForFinished(pollInterval))
        {
            waited += pollInterval;
            if (waited > 10000) // 10 seconds should be enough for the process to terminate
            {
                emit warningMessage(QObject::tr("The oscap process didn't terminate in time, it will be killed instead."));
                // if it didn't terminate, we have to kill it at this point
                process.kill();
                break;
            }
        }
    }
    else
    {
        // read everything left over
        readStdOut(process);
        watchStdErr(process);

        mResults = readRemoteFile(resultFile, QObject::tr("XCCDF results")).toUtf8();
        mReport = readRemoteFile(reportFile, QObject::tr("XCCDF report (HTML)")).toUtf8();
        mARF = readRemoteFile(arfFile, QObject::tr("Result DataStream (ARF)")).toUtf8();
    }

    emit infoMessage(QObject::tr("Cleaning up..."));

    // Remove all the temporary remote files
    removeRemoteFile(inputFile, QObject::tr("input file"));
    if (!tailoringFile.isEmpty())
        removeRemoteFile(tailoringFile, QObject::tr("tailoring file"));
    removeRemoteFile(resultFile, QObject::tr("XCCDF result file"));
    removeRemoteFile(reportFile, QObject::tr("XCCDF report file"));
    removeRemoteFile(arfFile, QObject::tr("Result DataStream file"));
    removeRemoteDirectory(workingDir, QObject::tr("Temporary Working Directory"));

    emit infoMessage(QObject::tr("Processing has been finished!"));
    signalCompletion(mCancelRequested);
}
void OscapScannerLocal::evaluate()
{
    if (mDryRun)
    {
        signalCompletion(mCancelRequested);
        return;
    }

    emit infoMessage(QObject::tr("Querying capabilities..."));

    {
        SyncProcess proc(this);
        proc.setCommand(SCAP_WORKBENCH_LOCAL_OSCAP_PATH);
        proc.setArguments(QStringList("--v"));
        proc.run();

        if (proc.getExitCode() != 0)
        {
            emit errorMessage(
                QObject::tr("Failed to query capabilities of oscap on local machine.\n"
                    "Diagnostic info:\n%1").arg(proc.getDiagnosticInfo())
            );

            mCancelRequested = true;
            signalCompletion(mCancelRequested);
            return;
        }

        mCapabilities.parse(proc.getStdOutContents());
    }

    if (!checkPrerequisites())
    {
        mCancelRequested = true;
        signalCompletion(mCancelRequested);
        return;
    }

    // TODO: Error handling!
    emit infoMessage(QObject::tr("Creating temporary files..."));

    QTemporaryFile resultFile;
    resultFile.setAutoRemove(true);
    // the following forces Qt to give us the filename
    resultFile.open(); resultFile.close();

    QTemporaryFile reportFile;
    reportFile.setAutoRemove(true);
    reportFile.open(); reportFile.close();

    QTemporaryFile arfFile;
    arfFile.setAutoRemove(true);
    arfFile.open(); arfFile.close();

    // This is mainly for check-engine-results and oval-results, to ensure
    // we get a full report, including info from these files. openscap's XSLT
    // uses info in the check engine results if it can find them.
    TemporaryDir workingDir;

    emit infoMessage(QObject::tr("Starting the oscap process..."));
    QProcess process(this);
    process.setWorkingDirectory(workingDir.getPath());

    QStringList args;

    QTemporaryFile inputARFFile;
    inputARFFile.setAutoRemove(true);

    if (mScannerMode == SM_OFFLINE_REMEDIATION)
    {
        inputARFFile.open();
        inputARFFile.write(getARFForRemediation());
        inputARFFile.close();

        args = buildOfflineRemediationArgs(inputARFFile.fileName(),
                resultFile.fileName(),
                reportFile.fileName(),
                arfFile.fileName());
    }
    else
    {
        args = buildEvaluationArgs(mSession->getOpenedFilePath(),
                mSession->hasTailoring() ? mSession->getTailoringFilePath() : QString(),
                resultFile.fileName(),
                reportFile.fileName(),
                arfFile.fileName(),
                mScannerMode == SM_SCAN_ONLINE_REMEDIATION);
    }

    QString program = "";
#ifdef SCAP_WORKBENCH_LOCAL_NICE_FOUND
    args.prepend(getPkexecOscapPath());
    args.prepend(QString::number(SCAP_WORKBENCH_LOCAL_OSCAP_NICENESS));
    args.prepend("-n");

    program = SCAP_WORKBENCH_LOCAL_NICE_PATH;
#else
    program = getPkexecOscapPath();
#endif

    process.start(program, args);
    process.waitForStarted();

    if (process.state() != QProcess::Running)
    {
        emit errorMessage(QObject::tr("Failed to start local scanning process '%1'. Perhaps the executable was not found?").arg(program));
        mCancelRequested = true;
    }

    const unsigned int pollInterval = 100;

    emit infoMessage(QObject::tr("Processing..."));
    while (!process.waitForFinished(pollInterval))
    {
        // read everything new
        readStdOut(process);
        watchStdErr(process);

        // pump the event queue, mainly because the user might want to cancel
        QAbstractEventDispatcher::instance(mScanThread)->processEvents(QEventLoop::AllEvents);

        if (mCancelRequested)
        {
            emit infoMessage(QObject::tr("Cancellation was requested! Terminating scanning..."));
            process.kill();
            process.waitForFinished(1000);
            break;
        }
    }

    if (!mCancelRequested)
    {
        if (process.exitCode() == 1) // error happened
        {
            watchStdErr(process);
            // TODO: pass the diagnostics over
            emit errorMessage(QObject::tr("There was an error during evaluation! Exit code of the 'oscap' process was 1."));
            // mark this run as canceled
            mCancelRequested = true;
        }
        else
        {
            // read everything left over
            readStdOut(process);
            watchStdErr(process);

            emit infoMessage(QObject::tr("The oscap tool has finished. Reading results..."));

            resultFile.open();
            mResults = resultFile.readAll();
            resultFile.close();

            reportFile.open();
            mReport = reportFile.readAll();
            reportFile.close();

            arfFile.open();
            mARF = arfFile.readAll();
            arfFile.close();

            emit infoMessage(QObject::tr("Processing has been finished!"));
        }
    }

    signalCompletion(mCancelRequested);
}