diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e7584ab1..7bcb8be9a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -360,8 +360,6 @@ if(BUILD_DESKTOP) part/dlgannotations.cpp part/dlgperformance.cpp part/dlgpresentation.cpp - part/dlgsignatures.cpp - part/certificatetools.cpp part/editannottooldialog.cpp part/editdrawingtooldialog.cpp part/widgetannottools.cpp @@ -433,7 +431,6 @@ ki18n_wrap_ui(okularpart_SRCS part/dlgannotationsbase.ui part/dlgperformancebase.ui part/dlgpresentationbase.ui - part/dlgsignaturesbase.ui ) kconfig_add_kcfg_files(okularpart_SRCS GENERATE_MOC conf/settings.kcfgc) diff --git a/conf/okular.kcfg b/conf/okular.kcfg index 62b5cc4de..ddf56f01e 100644 --- a/conf/okular.kcfg +++ b/conf/okular.kcfg @@ -398,8 +398,4 @@ userString - - - - diff --git a/core/document.cpp b/core/document.cpp index e6f4f4642..54a1b38cb 100644 --- a/core/document.cpp +++ b/core/document.cpp @@ -2734,6 +2734,11 @@ void Document::sign( const Okular::Annotation* pWhichAnnotation ) } } +Okular::CertificateStore* Document::getCertStore() +{ + return d->m_generator ? d->m_generator->getCertStore() : nullptr; +} + DocumentInfo Document::documentInfo() const { QSet keys; diff --git a/core/document.h b/core/document.h index bdc0315e4..2eaf2ac5e 100644 --- a/core/document.h +++ b/core/document.h @@ -38,6 +38,7 @@ namespace Okular { class Annotation; class BookmarkManager; +class CertificateStore; class DocumentInfoPrivate; class DocumentObserver; class DocumentPrivate; @@ -1057,8 +1058,21 @@ public Q_SLOTS: */ void reloadDocument() const; + /** + * Digitally sign document, as the passed annotation's place + * + * @since 1.9 + */ void sign( const Okular::Annotation* pWhichAnnotation ); + /** + * Returns the generator's certificate store (if any) + * + * @since 1.9 + */ + CertificateStore* getCertStore(); + + /** * Returns the part of document covered by the given signature @p info. * diff --git a/generators/poppler/CMakeLists.txt b/generators/poppler/CMakeLists.txt index 40406548a..1d4dbb814 100644 --- a/generators/poppler/CMakeLists.txt +++ b/generators/poppler/CMakeLists.txt @@ -38,14 +38,17 @@ set(okularGenerator_poppler_PART_SRCS generator_pdf.cpp formfields.cpp annots.cpp + certificatetools.cpp pdfsignatureutils.cpp ) ki18n_wrap_ui(okularGenerator_poppler_PART_SRCS conf/pdfsettingswidget.ui + conf/certsettingswidget.ui ) kconfig_add_kcfg_files(okularGenerator_poppler_PART_SRCS conf/pdfsettings.kcfgc ) +kconfig_add_kcfg_files(okularGenerator_poppler_PART_SRCS conf/certsettings.kcfgc ) okular_add_generator(okularGenerator_poppler ${okularGenerator_poppler_PART_SRCS}) diff --git a/generators/poppler/certificatetools.cpp b/generators/poppler/certificatetools.cpp new file mode 100644 index 000000000..e2397f306 --- /dev/null +++ b/generators/poppler/certificatetools.cpp @@ -0,0 +1,189 @@ +/*************************************************************************** + * Copyright (C) 2019 by Bubli * + * * + * 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. * + ***************************************************************************/ + +#include "certificatetools.h" +#include +#include +#include "certsettings.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +CertificateTools::CertificateTools( QWidget * parent ) + : QWidget( parent ) +{ + QHBoxLayout *hBoxLayout = new QHBoxLayout( this ); + m_list = new QListWidget( this ); + m_list->setIconSize( QSize( 32, 32 ) ); + hBoxLayout->addWidget( m_list ); + + QVBoxLayout *vBoxLayout = new QVBoxLayout(); + m_btnAdd = new QPushButton( i18n("&Add..."), this ); + m_btnAdd->setIcon( QIcon::fromTheme(QStringLiteral("list-add")) ); + vBoxLayout->addWidget( m_btnAdd ); + m_btnEdit = new QPushButton( i18n("&Edit..."), this ); + m_btnEdit->setIcon( QIcon::fromTheme(QStringLiteral("edit-rename")) ); + m_btnEdit->setEnabled( false ); + vBoxLayout->addWidget( m_btnEdit ); + m_btnRemove = new QPushButton( i18n("&Remove"), this ); + m_btnRemove->setIcon( QIcon::fromTheme(QStringLiteral("list-remove")) ); + m_btnRemove->setEnabled( false ); + vBoxLayout->addWidget( m_btnRemove ); + m_btnMoveUp = new QPushButton( i18n("Move &Up"), this ); + m_btnMoveUp->setIcon( QIcon::fromTheme(QStringLiteral("arrow-up")) ); + m_btnMoveUp->setEnabled( false ); + vBoxLayout->addWidget( m_btnMoveUp ); + m_btnMoveDown = new QPushButton( i18n("Move &Down"), this ); + m_btnMoveDown->setIcon( QIcon::fromTheme(QStringLiteral("arrow-down")) ); + m_btnMoveDown->setEnabled( false ); + vBoxLayout->addWidget( m_btnMoveDown ); + vBoxLayout->addStretch(); + hBoxLayout->addLayout( vBoxLayout ); + + connect(m_list, &QListWidget::itemDoubleClicked, this, &CertificateTools::slotEdit); + connect(m_list, &QListWidget::currentRowChanged, this, &CertificateTools::updateButtons); + connect(m_btnAdd, &QPushButton::clicked, this, &CertificateTools::slotAdd); + connect(m_btnEdit, &QPushButton::clicked, this, &CertificateTools::slotEdit); + connect(m_btnRemove, &QPushButton::clicked, this, &CertificateTools::slotRemove); + connect(m_btnMoveUp, &QPushButton::clicked, this, &CertificateTools::slotMoveUp); + connect(m_btnMoveDown, &QPushButton::clicked, this, &CertificateTools::slotMoveDown); +} + +CertificateTools::~CertificateTools() +{ +} + +QStringList CertificateTools::certificates() const +{ + QStringList res; + + const int count = m_list->count(); + for ( int i = 0; i < count; ++i ) + { + QListWidgetItem * listEntry = m_list->item(i); + res << listEntry->data(Qt::UserRole).toString(); + } + + return res; +} + +void CertificateTools::setCertificates(const QStringList& /*items*/) +{ + m_list->clear(); + +/* TODO: custom list of certs, perhaps from files? also permit ordering... + QStringList certs = CertificateSettings::certificates(); + foreach( const QString cert, certs ) + { + QListWidgetItem * listEntry = new QListWidgetItem( cert, m_list ); + (void)listEntry; + } +*/ + QVector nssCerts = Poppler::getAvailableSigningCertificates(); + foreach( auto cert, nssCerts ) + { + QListWidgetItem * listEntry = new QListWidgetItem( + cert->subjectInfo( + Poppler::CertificateInfo::EntityInfoKey::CommonName ) + "\t(" + + cert->validityEnd().toString("yyyy-MM-dd") + ")", + m_list ); + + QJsonObject json; + json["CommonName"] = cert->subjectInfo( Poppler::CertificateInfo::EntityInfoKey::CommonName ); + json["SerialNumber"] = QString::fromStdString(cert->serialNumber().toStdString()); + json["ValidUntil"] = cert->validityEnd().toString(); + listEntry->setData( Qt::UserRole, QJsonDocument(json).toJson() ); + } + + updateButtons(); +} + +void CertificateTools::slotAdd() +{ + QString certCN = QInputDialog::getText( this, i18n("Enter Certificate CN"), i18n("CertificateCN"), QLineEdit::Normal, QString() ); + + if (certCN.isEmpty()) + return; + + // Create list entry + QListWidgetItem * listEntry = new QListWidgetItem( certCN, m_list ); + + // Select and scroll + m_list->setCurrentItem( listEntry ); + m_list->scrollToItem( listEntry ); + updateButtons(); + emit changed(); +} + +void CertificateTools::slotEdit() +{ + QListWidgetItem *listEntry = m_list->currentItem(); + + bool ok; + QString certCN = QInputDialog::getText( this, i18n("Change Certificate CN"), i18n("CertificateCN"), QLineEdit::Normal, listEntry->text(), &ok ); + + if( ok ) + { + listEntry->setText(certCN); + + // Select and scrolldd + m_list->setCurrentItem( listEntry ); + m_list->scrollToItem( listEntry ); + updateButtons(); + emit changed(); + } +} + +void CertificateTools::updateButtons() +{ + const int row = m_list->currentRow(); + const int last = m_list->count() - 1; + + m_btnEdit->setEnabled( row != -1 ); + m_btnRemove->setEnabled( row != -1 ); + m_btnMoveUp->setEnabled( row > 0 ); + m_btnMoveDown->setEnabled( row != -1 && row != last ); +} + + +void CertificateTools::slotRemove() +{ + const int row = m_list->currentRow(); + delete m_list->takeItem(row); + updateButtons(); + emit changed(); +} + +void CertificateTools::slotMoveUp() +{ + const int row = m_list->currentRow(); + m_list->insertItem( row, m_list->takeItem(row-1) ); + m_list->scrollToItem( m_list->currentItem() ); + updateButtons(); + emit changed(); +} + +void CertificateTools::slotMoveDown() +{ + const int row = m_list->currentRow(); + m_list->insertItem( row, m_list->takeItem(row+1) ); + m_list->scrollToItem( m_list->currentItem() ); + updateButtons(); + emit changed(); +} diff --git a/part/certificatetools.h b/generators/poppler/certificatetools.h similarity index 51% rename from part/certificatetools.h rename to generators/poppler/certificatetools.h index 15dd03b59..e84d3c6d9 100644 --- a/part/certificatetools.h +++ b/generators/poppler/certificatetools.h @@ -10,23 +10,42 @@ #ifndef _CERTIFICATETOOLS_H_ #define _CERTIFICATETOOLS_H_ -#include "widgetconfigurationtoolsbase.h" +#include +class QListWidget; +class QPushButton; -#include - -class CertificateTools : public WidgetConfigurationToolsBase +class CertificateTools : public QWidget { Q_OBJECT + + Q_PROPERTY( QStringList certificates READ certificates WRITE setCertificates NOTIFY changed USER true ) + public: explicit CertificateTools( QWidget * parent = nullptr ); - ~CertificateTools() {}; + ~CertificateTools() override; + + QStringList certificates() const; + void setCertificates(const QStringList& items); + + Q_SIGNALS: + void changed(); - QStringList tools() const override; - void setTools(const QStringList& items) override; + protected: + QListWidget *m_list; + private: + QPushButton *m_btnAdd; + QPushButton *m_btnEdit; + QPushButton *m_btnRemove; + QPushButton *m_btnMoveUp; + QPushButton *m_btnMoveDown; protected Q_SLOTS: - void slotAdd() override; - void slotEdit() override; + void slotAdd(); + void slotEdit(); + void updateButtons(); + void slotRemove(); + void slotMoveUp(); + void slotMoveDown(); }; #endif diff --git a/generators/poppler/conf/certsettings.kcfg b/generators/poppler/conf/certsettings.kcfg new file mode 100644 index 000000000..ec95b1795 --- /dev/null +++ b/generators/poppler/conf/certsettings.kcfg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/generators/poppler/conf/certsettings.kcfgc b/generators/poppler/conf/certsettings.kcfgc new file mode 100644 index 000000000..5975c7842 --- /dev/null +++ b/generators/poppler/conf/certsettings.kcfgc @@ -0,0 +1,4 @@ +File=certsettings.kcfg +ClassName=CertificateSettings +Mutators=true +Singleton=true diff --git a/part/dlgsignaturesbase.ui b/generators/poppler/conf/certsettingswidget.ui similarity index 100% rename from part/dlgsignaturesbase.ui rename to generators/poppler/conf/certsettingswidget.ui diff --git a/generators/poppler/generator_pdf.cpp b/generators/poppler/generator_pdf.cpp index 0769fdf3f..64e7a117e 100644 --- a/generators/poppler/generator_pdf.cpp +++ b/generators/poppler/generator_pdf.cpp @@ -36,6 +36,7 @@ #include #include +#include #include #include @@ -49,10 +50,17 @@ #include #include #include +#include #include "pdfsettings.h" #include "ui_pdfsettingswidget.h" +#include "certificatetools.h" +#include "ui_certsettingswidget.h" +#include "certsettings.h" + +#include + #include #include @@ -60,6 +68,7 @@ #include "debug_pdf.h" #include "formfields.h" #include "popplerembeddedfile.h" +#include "pdfsignatureutils.h" Q_DECLARE_METATYPE(Poppler::Annotation *) Q_DECLARE_METATYPE(Poppler::FontInfo) @@ -544,6 +553,7 @@ PDFGenerator::PDFGenerator(QObject *parent, const QVariantList &args) , docEmbeddedFilesDirty(true) , nextFontPage(0) , annotProxy(nullptr) + , certStore( nullptr ) { setFeature(Threaded); setFeature(TextExtraction); @@ -1468,6 +1478,17 @@ void PDFGenerator::addPages(KConfigDialog *dlg) QWidget *w = new QWidget(dlg); pdfsw.setupUi(w); dlg->addPage(w, PDFSettings::self(), i18n("PDF"), QStringLiteral("application-pdf"), i18n("PDF Backend Configuration")); + + Ui_DlgSignaturesBase certsw; + QWidget* w2 = new QWidget(dlg); + certsw.setupUi(w2); + + CertificateTools * kcfg_CertTools = new CertificateTools( certsw.certificatesGroup ); + certsw.certificatesPlaceholder->addWidget( kcfg_CertTools ); + kcfg_CertTools->setObjectName( QStringLiteral("kcfg_Certificates") ); + KConfigDialogManager::changedMap()->insert( QStringLiteral("CertificateTools"), SIGNAL(changed()) ); + + dlg->addPage(w2, CertificateSettings::self(), i18n("Certificates"), QStringLiteral("application-pkcs7-signature"), i18n("Digital Signature Certificates") ); } bool PDFGenerator::setDocumentRenderHints() @@ -1885,6 +1906,33 @@ bool PDFGenerator::sign( const Okular::Annotation* pWhichAnnotation, const QStri return true; } +namespace { + struct CertificateStoreImpl : public Okular::CertificateStore + { + virtual QList getSigningCertificates() const + { + QVector certs = Poppler::getAvailableSigningCertificates(); + QList vReturnCerts; + for (auto cert : certs) + vReturnCerts.append(new PopplerCertificateInfo(*cert)); + + return vReturnCerts; + } + }; +} + +Okular::CertificateStore* PDFGenerator::getCertStore() +{ +#ifdef HAVE_POPPLER_SIGNING + if( !certStore ) + certStore = new CertificateStoreImpl(); + + return certStore; +#else + return nullptr; +#endif +} + #include "generator_pdf.moc" Q_LOGGING_CATEGORY(OkularPdfDebug, "org.kde.okular.generators.pdf", QtWarningMsg) diff --git a/generators/poppler/generator_pdf.h b/generators/poppler/generator_pdf.h index 1ac98d568..01a0e8a76 100644 --- a/generators/poppler/generator_pdf.h +++ b/generators/poppler/generator_pdf.h @@ -143,6 +143,7 @@ private: mutable QList docEmbeddedFiles; int nextFontPage; PopplerAnnotationProxy *annotProxy; + Okular::CertificateStore* certStore; // the hash below only contains annotations that were present on the file at open time // this is enough for what we use it for QHash annotationsOnOpenHash; diff --git a/part/certificatetools.cpp b/part/certificatetools.cpp deleted file mode 100644 index e8d68c682..000000000 --- a/part/certificatetools.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2019 by Bubli * - * * - * 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. * - ***************************************************************************/ - -#include "certificatetools.h" -#include -#include -#include "settings.h" - -#include -#include -#include - -CertificateTools::CertificateTools( QWidget * parent ) - : WidgetConfigurationToolsBase( parent ) -{ -} - -QStringList CertificateTools::tools() const -{ - QStringList res; - - const int count = m_list->count(); - for ( int i = 0; i < count; ++i ) - { - QListWidgetItem * listEntry = m_list->item(i); - - res << listEntry->text(); - } - - return res; -} - -void CertificateTools::setTools(const QStringList& /*items*/) -{ - m_list->clear(); - - QStringList certs = Okular::Settings::certificates(); - foreach( const QString cert, certs ) - { - QListWidgetItem * listEntry = new QListWidgetItem( cert, m_list ); - (void)listEntry; - } - //QVector nssCerts = Poppler::getAvailableSigningCertificates(); - Poppler::getAvailableSigningCertificates(); - foreach( auto cert, nssCerts ) - { - QListWidgetItem * listEntry = new QListWidgetItem( cert->getSubjectInfo().commonName, - m_list ); - (void)listEntry; - } - - updateButtons(); -} - -void CertificateTools::slotAdd() -{ - QString certCN = QInputDialog::getText( this, i18n("Enter Certificate CN"), i18n("CertificateCN"), QLineEdit::Normal, QString() ); - - if (certCN.isEmpty()) - return; - - // Create list entry - QListWidgetItem * listEntry = new QListWidgetItem( certCN, m_list ); - - // Select and scroll - m_list->setCurrentItem( listEntry ); - m_list->scrollToItem( listEntry ); - updateButtons(); - emit changed(); -} - -void CertificateTools::slotEdit() -{ - QListWidgetItem *listEntry = m_list->currentItem(); - - QString certCN = QInputDialog::getText( this, i18n("Change Certificate CN"), i18n("CertificateCN"), QLineEdit::Normal, listEntry->text() ); - listEntry->setText(certCN); - - // Select and scrolldd - m_list->setCurrentItem( listEntry ); - m_list->scrollToItem( listEntry ); - updateButtons(); - emit changed(); -} diff --git a/part/dlgsignatures.cpp b/part/dlgsignatures.cpp deleted file mode 100644 index 36e52c15e..000000000 --- a/part/dlgsignatures.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2019 by Bubli * - * * - * 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. * - ***************************************************************************/ - -#include "dlgsignatures.h" - -#include "certificatetools.h" -#include "ui_dlgsignaturesbase.h" - -#include - -DlgSignatures::DlgSignatures(QWidget *parent) - : QWidget(parent) -{ - m_dlg = new Ui_DlgSignaturesBase(); - m_dlg->setupUi(this); - - CertificateTools * kcfg_CertTools = new CertificateTools( m_dlg->certificatesGroup ); - m_dlg->certificatesPlaceholder->addWidget( kcfg_CertTools ); - kcfg_CertTools->setObjectName( QStringLiteral("kcfg_Certificates") ); - - KConfigDialogManager::changedMap()->insert( QStringLiteral("CertificateTools"), SIGNAL(changed()) ); -} - -DlgSignatures::~DlgSignatures() -{ - delete m_dlg; -} -#include "moc_dlgsignatures.cpp" diff --git a/part/dlgsignatures.h b/part/dlgsignatures.h deleted file mode 100644 index f54fd04c8..000000000 --- a/part/dlgsignatures.h +++ /dev/null @@ -1,29 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2019 by Bubli * - * * - * 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. * - ***************************************************************************/ - -#ifndef DLGSIGNATURES_H -#define DLGSIGNATURES_H - -#include - -class Ui_DlgSignaturesBase; - -class DlgSignatures : public QWidget -{ - Q_OBJECT - -public: - explicit DlgSignatures(QWidget *parent = nullptr); - virtual ~DlgSignatures(); - -private: - Ui_DlgSignaturesBase *m_dlg; -}; - -#endif diff --git a/part/pageviewannotator.cpp b/part/pageviewannotator.cpp index 090d5c55f..1fc186b6a 100644 --- a/part/pageviewannotator.cpp +++ b/part/pageviewannotator.cpp @@ -40,6 +40,7 @@ #include "core/area.h" #include "core/document.h" #include "core/page.h" +#include "core/signatureutils.h" #include "debug_ui.h" #include "editannottooldialog.h" #include "guiutils.h" @@ -313,15 +314,15 @@ private: class PickPointEngine2 : public PickPointEngine { -public: - PickPointEngine2(const QDomElement &engineElement) - : PickPointEngine(engineElement) - { - clicked = false; - m_block = true; - xscale = 1.0; - yscale = 1.0; - } + public: + PickPointEngine2( const QDomElement & engineElement, Okular::Document* storage ) + : PickPointEngine( engineElement ), m_document(storage) + { + clicked = false; + m_block = true; + xscale = 1.0; + yscale = 1.0; + } QRect event(EventType type, Button button, Modifiers modifiers, double nX, double nY, double xScale, double yScale, const Okular::Page *page) override { @@ -330,10 +331,15 @@ public: QList end() override { - QStringList items = Okular::Settings::certificates(); - Okular::Annotation * ann = nullptr; + Okular::CertificateStore* certStore = m_document->getCertStore(); + QList certs = certStore->getSigningCertificates(); + + QStringList items; + for( auto cert : certs ) + items.append(cert->subjectInfo( Okular::CertificateInfo::EntityInfoKey::CommonName )); + bool resok = false; QString cert = QInputDialog::getItem(nullptr, i18n( "Select certificate to sign with" ), i18n( "Certificates:" ), items, 0, false, &resok); @@ -368,6 +374,9 @@ public: return QList< Okular::Annotation* >() << ann; } + + private: + Okular::Document* m_document; }; /** @short PolyLineEngine */ @@ -885,7 +894,7 @@ QRect PageViewAnnotator::performRouteMouseOrTabletEvent(const AnnotatorEngine::E QDomElement elem; elem.setTagName("engine"); elem.setAttribute("block", 1); - m_engine = new PickPointEngine2(elem); + m_engine = new PickPointEngine2(elem, m_document); } // 1. lock engine to current item diff --git a/part/preferencesdialog.cpp b/part/preferencesdialog.cpp index d187d934e..a34ece3de 100644 --- a/part/preferencesdialog.cpp +++ b/part/preferencesdialog.cpp @@ -11,7 +11,6 @@ #include "preferencesdialog.h" #include -#include "../generators/poppler/config-okular-poppler.h" // single config pages #include "dlgaccessibility.h" @@ -21,7 +20,6 @@ #include "dlggeneral.h" #include "dlgperformance.h" #include "dlgpresentation.h" -#include "dlgsignatures.h" PreferencesDialog::PreferencesDialog(QWidget *parent, KConfigSkeleton *skeleton, Okular::EmbedMode embedMode) : KConfigDialog(parent, QStringLiteral("preferences"), skeleton) @@ -49,15 +47,9 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, KConfigSkeleton *skeleton, m_presentation = new DlgPresentation(this); m_annotations = new DlgAnnotations(this); m_editor = new DlgEditor(this); -#ifdef HAVE_POPPLER_SIGNING - m_signatures = new DlgSignatures(this); -#endif addPage(m_presentation, i18n("Presentation"), QStringLiteral("view-presentation"), i18n("Options for Presentation Mode")); m_annotationsPage = addPage(m_annotations, i18n("Annotations"), QStringLiteral("draw-freehand"), i18n("Annotation Options")); addPage(m_editor, i18n("Editor"), QStringLiteral("accessories-text-editor"), i18n("Editor Options")); -#ifdef HAVE_POPPLER_SIGNING - addPage(m_signatures, i18n("Signatures"), QStringLiteral("application-pkcs7-signature"), i18n("Digital Signatures")); -#endif } #ifdef OKULAR_DEBUG_CONFIGPAGE addPage(m_debug, "Debug", "system-run", "Debug options");