Commits vergleichen
17 Commits
fc92b963d4
...
399c27a452
Autor | SHA1 | Datum | |
---|---|---|---|
399c27a452 | |||
7a2e4aaec1 | |||
11decb757f | |||
b71c234fb4 | |||
bc908a1038 | |||
8ba4ee5847 | |||
451c79088a | |||
1a39118470 | |||
0d2e518000 | |||
b108cb7151 | |||
c82cf03814 | |||
8a82da95bd | |||
a408173064 | |||
dd479fa668 | |||
e76988ee77 | |||
f29f997289 | |||
afa9d33f3d |
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,5 +1,18 @@
|
||||
# looqs: Release notes
|
||||
|
||||
## 2022-06-14 - v0.3
|
||||
CHANGES:
|
||||
- GUI: Add settings tab to configure various settings
|
||||
- GUI: Don't render previews for results that do not originate from a content search. This was confusing.
|
||||
- GUI: Remove 'open config file' menu action
|
||||
- GUI: Highlight "words" that are actually numbers in preview texts
|
||||
- search: Search for "words" that are a number, e. g. 23. Previously, those were ignored due to a regression.
|
||||
- CLI: Minor cleanups.
|
||||
- sandbox: Improve preview generation sandbox further by restricting it more
|
||||
- sandbox: Fix case where activiation would fail during indexing in some cases on kernels without landlock.
|
||||
- Remove 'firstrun' setting
|
||||
|
||||
|
||||
## 2022-06-07 - v0.2
|
||||
CHANGES:
|
||||
- Sandboxing: Add environment variable `LOOQS_DISABLE_SANDBOXING` to disable sandboxing. This is intended for troubleshooting
|
||||
|
18
HACKING.md
18
HACKING.md
@ -3,31 +3,31 @@
|
||||
## Introduction
|
||||
Without elaborating here, I hacked looqs because I was not satisfied with the state of desktop search on Linux.
|
||||
|
||||
Originally a set of CLI python scripts, it is now written in C++ and offers a GUI made using Qt. While a "web app" would have been an option, I prefer a desktop application for something like looqs. I chose Qt because I am more familiar with it than with any other GUI framework. To my knowledge, potential alternatives like GTK do not include as many "batteries" as Qt anyway, so the job presumably would have been harder there.
|
||||
Originally a set of CLI python scripts, it is now written in C++ and offers a GUI made using Qt. While a "web app" would have been an option, I prefer a desktop application for something like looqs. I chose Qt because I am more familiar with it than with any other GUI framework. To my knowledge, potential alternatives like GTK do not include as many "batteries" as Qt anyway, so the job presumably would have been harder there,
|
||||
at least for me.
|
||||
|
||||
[CONTRIBUTING.md](CONTRIBUTING.md) contains the instructions on how to submit patches etc.
|
||||
If you are interested in how to contribute, please see the file [CONTRIBUTING.md](CONTRIBUTING.md) which contains the instructions on how to submit patches etc.
|
||||
|
||||
## Security
|
||||
The architecture ensures that the parsing of documents and the preview generation is sandboxed by [exile.h](https://github.com/quitesimpleorg/exile.h). looqs uses a multi-process architecture to achieve this.
|
||||
The architecture ensures that the parsing of documents and the preview generation is sandboxed by [exile.h](https://github.com/quitesimpleorg/exile.h). looqs uses a multi-process architecture to achieve this.
|
||||
|
||||
Qt code is considered trusted in this model. While one may critize this, it was the only practical solution. looqs uses its serialization mechanism and other classes to communicate between the non-sandboxed GUI process and the sandboxed processes.
|
||||
Qt code is considered trusted in this model. While one may critize this, it was the only practical solution. looqs uses Qt's serialization mechanism and other classes to communicate between the non-sandboxed GUI process and the sandboxed processes.
|
||||
|
||||
Set the enviornment variable `LOOQS_DISABLE_SANDBOX=1` to disable sandboxing. It's intended for troublehshooting.
|
||||
|
||||
## Database
|
||||
The heart is sqlite, with the FTS5 extensions behind the full-text search. I definitly did not
|
||||
want to run some heavy Java based solutions. I explored other options like Postgresql, I've discard them due to some limitations back then.
|
||||
The heart is sqlite, with the FTS5 extensions behind the full-text search. While FTS may not be sqlite's strong suit, I definitly did not want to run one of those oftenly recommended heavy (Java based) solutions. I explored other options like Postgresql, I've discard them due to some limitations back then.
|
||||
|
||||
Down the road, alternatives will be explored of course if sqlite should not suffice anymore.
|
||||
|
||||
## File format support
|
||||
The pdf library is libpoppler. Files such as .odt or .docx documents are opened with libquazip. The XML files in there are not parsed,
|
||||
looqs simply strips the tags and that seems to work fine so far. Naturally, this is not the "proper way", so there is room for improvement maybehere. However, those file formats are not a huge priortiy for me personally. libuchardet does encoding detection and conversion.
|
||||
looqs simply strips the tags and that seems to work fine so far. Naturally, this is not the "proper way", so there is room for improvement maybe here. However, those file formats are not a huge priortiy for me personally. libuchardet does encoding detection and conversion.
|
||||
|
||||
Naturally looqs won't be able to index and render previews for everything. Such approach would create a huge bloated binary. In the future, there will be some plugin system of some sorts, either we will load .so objects or use subprocesses.
|
||||
Naturally, looqs won't be able to index and render previews for everything. Such approach would create a huge bloated binary. In the future, there will be some plugin system of some sorts, either we will load .so objects or use subprocesses.
|
||||
|
||||
## Name
|
||||
looqs looks for files. You as the user can also look inside them. The 'k' was replaced by a 'q'. Originally wanted my projects to have "qs" (for quitesimple) in their name. While abandoned now, this got us to looqs.
|
||||
looqs looks for files. You as the user can also look inside them. The 'k' in "looks" was replaced by a 'q'. Originally, I wanted my projects to have "qs" (for quitesimple) in their name. While abandoned now, this got us to looqs.
|
||||
|
||||
|
||||
|
||||
|
@ -30,7 +30,7 @@ There is no need to write the long form of filters. There are also booleans avai
|
||||
|
||||
|
||||
## Current status
|
||||
Last version: 2022-06-07, v0.2
|
||||
Last version: 2022-06-14, v0.3
|
||||
|
||||
Please see [Changelog](CHANGELOG.md) for a human readable list of changes.
|
||||
|
||||
|
33
gui/main.cpp
33
gui/main.cpp
@ -26,8 +26,7 @@ void enableIpcSandbox()
|
||||
policy->namespace_options = EXILE_UNSHARE_USER | EXILE_UNSHARE_MOUNT | EXILE_UNSHARE_NETWORK;
|
||||
policy->no_new_privs = 1;
|
||||
policy->drop_caps = 1;
|
||||
policy->vow_promises =
|
||||
exile_vows_from_str("thread cpath wpath rpath unix stdio prot_exec proc shm fsnotify ioctl error");
|
||||
policy->vow_promises = exile_vows_from_str("thread cpath rpath unix stdio proc error");
|
||||
policy->mount_path_policies_to_chroot = 1;
|
||||
|
||||
QString ipcSocketPath = Common::ipcSocketPath();
|
||||
@ -35,6 +34,12 @@ void enableIpcSandbox()
|
||||
QString ipcSocketPathDir = info.absolutePath();
|
||||
std::string stdIpcSocketPath = ipcSocketPathDir.toStdString();
|
||||
|
||||
/* we only need the 'server' side of the 'unix' vow (for unix sockets)'. The process
|
||||
* has no business to connect anywhere.
|
||||
*
|
||||
* Maybe this case should be handled by exile at some point, but for now deal with it here */
|
||||
exile_append_syscall_policy(policy, EXILE_SYS(connect), EXILE_SYSCALL_DENY_RET_ERROR, NULL, 0);
|
||||
|
||||
/* ALLOW_EXEC is needed for fallback, not in landlock mode. It does not allow executing anything though here
|
||||
* due to the vows */
|
||||
exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ | EXILE_FS_ALLOW_EXEC, "/");
|
||||
@ -43,9 +48,26 @@ void enableIpcSandbox()
|
||||
int ret = exile_enable_policy(policy);
|
||||
if(ret != 0)
|
||||
{
|
||||
qDebug() << "Failed to establish sandbox";
|
||||
qDebug() << "Failed to establish sandbox" << Qt::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Arguments are irrelevant for sandbox test, just want to silence analyzer/compiler warnings */
|
||||
ret = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if(ret != -1 || errno != EACCES)
|
||||
{
|
||||
qCritical() << "Sandbox sanity check failed" << Qt::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const struct sockaddr *addr = {};
|
||||
ret = connect(3, addr, sizeof(*addr));
|
||||
if(ret != -1 || errno != EACCES)
|
||||
{
|
||||
qCritical() << "Sandbox sanity check failed" << Qt::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
exile_free_policy(policy);
|
||||
}
|
||||
|
||||
@ -66,7 +88,7 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
enableIpcSandbox();
|
||||
}
|
||||
QApplication a(argc, argv);
|
||||
QCoreApplication a(argc, argv);
|
||||
|
||||
IpcServer *ipcserver = new IpcServer();
|
||||
qDebug() << "Launching IPC Server";
|
||||
@ -120,7 +142,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
Common::setupAppInfo();
|
||||
QCommandLineParser parser;
|
||||
parser.addOption({{"s", "no-sandbox"}, "Disable sandboxing"});
|
||||
QStringList appArgs;
|
||||
for(int i = 0; i < argc; i++)
|
||||
{
|
||||
@ -128,6 +149,7 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
parser.parse(appArgs);
|
||||
|
||||
QApplication a(argc, argv);
|
||||
try
|
||||
{
|
||||
Common::ensureConfigured();
|
||||
@ -138,7 +160,6 @@ int main(int argc, char *argv[])
|
||||
QMessageBox::critical(nullptr, "Error", e.message);
|
||||
return 1;
|
||||
}
|
||||
QApplication a(argc, argv);
|
||||
a.setWindowIcon(QIcon(":/looqs.svg"));
|
||||
QObject::connect(&a, &QApplication::aboutToQuit, &process, &QProcess::kill);
|
||||
|
||||
|
@ -192,12 +192,6 @@ void MainWindow::connectSignals()
|
||||
|
||||
QMessageBox::about(this, "About looqs", html);
|
||||
});
|
||||
connect(ui->menuOpenConfigInTextEditorAction, &QAction::triggered, this,
|
||||
[this](bool checked)
|
||||
{
|
||||
QSettings setting;
|
||||
QDesktopServices::openUrl(setting.fileName());
|
||||
});
|
||||
connect(ui->menuAboutQtAction, &QAction::triggered, this,
|
||||
[this](bool checked) { QMessageBox::aboutQt(this, "About Qt"); });
|
||||
connect(ui->menuSyncIndexAction, &QAction::triggered, this, &MainWindow::startIndexSync);
|
||||
@ -215,6 +209,7 @@ void MainWindow::connectSignals()
|
||||
});
|
||||
connect(this, &MainWindow::beginIndexSync, indexSyncer, &IndexSyncer::sync);
|
||||
connect(&this->progressDialog, &QProgressDialog::canceled, indexSyncer, &IndexSyncer::cancel);
|
||||
connect(ui->btnSaveSettings, &QPushButton::clicked, this, &MainWindow::saveSettings);
|
||||
}
|
||||
|
||||
void MainWindow::startIndexSync()
|
||||
@ -236,6 +231,7 @@ void MainWindow::startIndexSync()
|
||||
|
||||
emit beginIndexSync();
|
||||
}
|
||||
|
||||
void MainWindow::spinPreviewPageValueChanged(int val)
|
||||
{
|
||||
makePreviews(val);
|
||||
@ -253,6 +249,7 @@ void MainWindow::startIndexing()
|
||||
|
||||
ui->previewsTab->setEnabled(false);
|
||||
ui->resultsTab->setEnabled(false);
|
||||
ui->settingsTab->setEnabled(false);
|
||||
ui->txtPathScanAdd->setEnabled(false);
|
||||
ui->txtSearch->setEnabled(false);
|
||||
ui->previewProcessBar->setValue(0);
|
||||
@ -289,6 +286,7 @@ void MainWindow::finishIndexing()
|
||||
ui->btnStartIndexing->setText("Start indexing");
|
||||
ui->previewsTab->setEnabled(true);
|
||||
ui->resultsTab->setEnabled(true);
|
||||
ui->settingsTab->setEnabled(true);
|
||||
ui->txtPathScanAdd->setEnabled(true);
|
||||
ui->txtSearch->setEnabled(true);
|
||||
}
|
||||
@ -351,6 +349,49 @@ void MainWindow::tabChanged()
|
||||
makePreviews(ui->spinPreviewPage->value());
|
||||
}
|
||||
}
|
||||
/* Settings tab active */
|
||||
if(ui->tabWidget->currentIndex() == 3)
|
||||
{
|
||||
initSettingsTabs();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::initSettingsTabs()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
QString pdfViewerCmd = settings.value(SETTINGS_KEY_PDFVIEWER).toString();
|
||||
QString excludedPaths = Common::excludedPaths().join(';');
|
||||
QString mountPaths = Common::mountPaths().join(';');
|
||||
QString databasePath = Common::databasePath();
|
||||
int numPagesPerPreview = settings.value(SETTINGS_KEY_PREVIEWSPERPAGE, 20).toInt();
|
||||
|
||||
ui->txtSettingPdfPreviewerCmd->setText(pdfViewerCmd);
|
||||
ui->txtSettingIgnoredPaths->setText(excludedPaths);
|
||||
ui->txtSettingMountPaths->setText(mountPaths);
|
||||
ui->spinSettingNumerPerPages->setValue(numPagesPerPreview);
|
||||
ui->txtSettingDatabasePath->setText(databasePath);
|
||||
}
|
||||
|
||||
void MainWindow::saveSettings()
|
||||
{
|
||||
QSettings settings;
|
||||
|
||||
QString pdfViewerCmd = ui->txtSettingPdfPreviewerCmd->text();
|
||||
QStringList excludedPaths = ui->txtSettingIgnoredPaths->text().split(';');
|
||||
QStringList mountPaths = ui->txtSettingMountPaths->text().split(';');
|
||||
QString databasePath = ui->txtSettingDatabasePath->text();
|
||||
|
||||
settings.setValue(SETTINGS_KEY_PDFVIEWER, pdfViewerCmd);
|
||||
settings.setValue(SETTINGS_KEY_EXCLUDEDPATHS, excludedPaths);
|
||||
settings.setValue(SETTINGS_KEY_MOUNTPATHS, mountPaths);
|
||||
settings.setValue(SETTINGS_KEY_PREVIEWSPERPAGE, ui->spinSettingNumerPerPages->value());
|
||||
settings.setValue(SETTINGS_KEY_DBPATH, databasePath);
|
||||
|
||||
settings.sync();
|
||||
|
||||
QProcess::startDetached(qApp->arguments()[0], qApp->arguments().mid(1));
|
||||
qApp->quit();
|
||||
}
|
||||
|
||||
void MainWindow::previewReceived(QSharedPointer<PreviewResult> preview, unsigned int previewGeneration)
|
||||
@ -389,7 +430,7 @@ void MainWindow::lineEditReturnPressed()
|
||||
ui->lblSearchResults->setText("Invalid paranthesis");
|
||||
return;
|
||||
}
|
||||
if(indexerTabActive())
|
||||
if(ui->tabWidget->currentIndex() > 1)
|
||||
{
|
||||
ui->tabWidget->setCurrentIndex(0);
|
||||
}
|
||||
@ -454,6 +495,11 @@ void MainWindow::handleSearchResults(const QVector<SearchResult> &results)
|
||||
bool exists = pathInfo.exists();
|
||||
if(exists)
|
||||
{
|
||||
if(!result.wasContentSearch)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!pathInfo.suffix().contains("htm")) // hack until we can preview them properly...
|
||||
{
|
||||
if(PreviewGenerator::get(pathInfo) != nullptr)
|
||||
@ -502,7 +548,7 @@ void MainWindow::makePreviews(int page)
|
||||
scaleText.chop(1);
|
||||
|
||||
QVector<QString> wordsToHighlight;
|
||||
QRegularExpression extractor(R"#("([^"]*)"|(\p{L}+))#");
|
||||
QRegularExpression extractor(R"#("([^"]*)"|((\p{L}|\p{N})+))#");
|
||||
for(const Token &token : this->contentSearchQuery.getTokens())
|
||||
{
|
||||
if(token.type == FILTER_CONTENT_CONTAINS)
|
||||
|
@ -61,6 +61,7 @@ class MainWindow : public QMainWindow
|
||||
void openDocument(QString path, int num);
|
||||
void openFile(QString path);
|
||||
unsigned int currentPreviewGeneration = 1;
|
||||
void initSettingsTabs();
|
||||
private slots:
|
||||
void lineEditReturnPressed();
|
||||
void treeSearchItemActivated(QTreeWidgetItem *item, int i);
|
||||
@ -73,6 +74,7 @@ class MainWindow : public QMainWindow
|
||||
void finishIndexing();
|
||||
void addPathToIndex();
|
||||
void startIndexSync();
|
||||
void saveSettings();
|
||||
|
||||
signals:
|
||||
void startIpcPreviews(RenderConfig config, const QVector<RenderTarget> &targets);
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1221</width>
|
||||
<height>709</height>
|
||||
<width>1280</width>
|
||||
<height>736</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -27,7 +27,7 @@
|
||||
<enum>QTabWidget::South</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>3</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="resultsTab">
|
||||
<attribute name="title">
|
||||
@ -81,8 +81,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1185</width>
|
||||
<height>419</height>
|
||||
<width>1244</width>
|
||||
<height>446</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout"/>
|
||||
@ -344,6 +344,145 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="settingsTab">
|
||||
<attribute name="title">
|
||||
<string>Settings</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupPdfSettings">
|
||||
<property name="title">
|
||||
<string>PDF Viewer</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Command to open PDF pages in (%f = file path; %p = page number)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txtSettingPdfPreviewerCmd"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupMountPaths">
|
||||
<property name="title">
|
||||
<string>Mount paths</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblMountPaths">
|
||||
<property name="text">
|
||||
<string>Path prefixes of files that should not be removed during sync, even if they cannot be accessed (e . g. files indexed on external disks) . Separated by ;</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txtSettingMountPaths"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Ignored paths</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Path prefixes that should always be ignored during indexing (will be applied before the ignore patterns). Separated by ;</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txtSettingIgnoredPaths"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupSettingDatabasePath">
|
||||
<property name="title">
|
||||
<string>Database path</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
||||
<item>
|
||||
<widget class="QLabel" name="lblSettingDatabasePath">
|
||||
<property name="text">
|
||||
<string>Path of the database. Note: Changing it here will create an empty, new database. The old will not be moved. </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="txtSettingDatabasePath"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="Misc">
|
||||
<property name="title">
|
||||
<string>Misc</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Max number of previews per 'page' in 'Previews' tab: </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="spinSettingNumerPerPages"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="btnSaveSettings">
|
||||
<property name="text">
|
||||
<string>Save settings and restart</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
@ -368,7 +507,7 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1221</width>
|
||||
<width>1280</width>
|
||||
<height>35</height>
|
||||
</rect>
|
||||
</property>
|
||||
@ -376,7 +515,6 @@
|
||||
<property name="title">
|
||||
<string>looqs</string>
|
||||
</property>
|
||||
<addaction name="menuOpenConfigInTextEditorAction"/>
|
||||
<addaction name="menuSyncIndexAction"/>
|
||||
<addaction name="menuAboutAction"/>
|
||||
<addaction name="menuAboutQtAction"/>
|
||||
@ -389,11 +527,6 @@
|
||||
<string>About</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menuOpenConfigInTextEditorAction">
|
||||
<property name="text">
|
||||
<string>Open config in text editor</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="menuAboutQtAction">
|
||||
<property name="text">
|
||||
<string>About Qt</string>
|
||||
|
@ -14,13 +14,6 @@
|
||||
#include "databasefactory.h"
|
||||
#include "logger.h"
|
||||
|
||||
#define SETTINGS_KEY_DBPATH "dbpath"
|
||||
#define SETTINGS_KEY_FIRSTRUN "firstrun"
|
||||
#define SETTINGS_KEY_IPCSOCKETPATH "ipcsocketpath"
|
||||
#define SETTINGS_KEY_PDFVIEWER "pdfviewer"
|
||||
#define SETTINGS_KEY_EXCLUDEDPATHS "excludedpaths"
|
||||
#define SETTINGS_KEY_MOUNTPATHS "mountpaths"
|
||||
|
||||
inline void initResources()
|
||||
{
|
||||
Q_INIT_RESOURCE(migrations);
|
||||
@ -91,10 +84,10 @@ void Common::setPdfViewer()
|
||||
void Common::ensureConfigured()
|
||||
{
|
||||
QSettings settings;
|
||||
QVariant firstRun = settings.value(SETTINGS_KEY_FIRSTRUN);
|
||||
if(!firstRun.isValid())
|
||||
QString dbpath = databasePath();
|
||||
if(dbpath == "")
|
||||
{
|
||||
QString dbpath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
|
||||
dbpath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
|
||||
QDir dir;
|
||||
if(!dir.exists(dbpath))
|
||||
{
|
||||
@ -104,38 +97,34 @@ void Common::ensureConfigured()
|
||||
}
|
||||
}
|
||||
dbpath += "/looqs.sqlite";
|
||||
}
|
||||
if(!QFile::exists(dbpath))
|
||||
{
|
||||
if(!initSqliteDatabase(dbpath))
|
||||
{
|
||||
throw LooqsGeneralException("Failed to initialize sqlite database");
|
||||
}
|
||||
|
||||
settings.setValue(SETTINGS_KEY_FIRSTRUN, false);
|
||||
settings.setValue(SETTINGS_KEY_DBPATH, dbpath);
|
||||
setPdfViewer();
|
||||
}
|
||||
else
|
||||
DatabaseFactory factory{dbpath};
|
||||
auto db = factory.forCurrentThread();
|
||||
DBMigrator migrator{db};
|
||||
if(migrator.migrationNeeded())
|
||||
{
|
||||
|
||||
QString dbpath = databasePath();
|
||||
if(!QFile::exists(dbpath))
|
||||
{
|
||||
throw LooqsGeneralException("Database " + dbpath + " was not found");
|
||||
}
|
||||
DatabaseFactory factory{dbpath};
|
||||
auto db = factory.forCurrentThread();
|
||||
DBMigrator migrator{db};
|
||||
if(migrator.migrationNeeded())
|
||||
{
|
||||
QFile out;
|
||||
out.open(stderr, QIODevice::WriteOnly);
|
||||
Logger migrationLogger{&out};
|
||||
migrationLogger << "Database is being upgraded, please be patient..." << Qt::endl;
|
||||
QObject::connect(&migrator, &DBMigrator::migrationDone,
|
||||
[&migrationLogger](uint32_t migration)
|
||||
{ migrationLogger << "Progress: Successfully migrated to: " << migration << Qt::endl; });
|
||||
migrator.performMigrations();
|
||||
migrationLogger << "Database upgraded successfully" << Qt::endl;
|
||||
}
|
||||
QFile out;
|
||||
out.open(stderr, QIODevice::WriteOnly);
|
||||
Logger migrationLogger{&out};
|
||||
migrationLogger << "Database is being upgraded, please be patient..." << Qt::endl;
|
||||
QObject::connect(&migrator, &DBMigrator::migrationDone,
|
||||
[&migrationLogger](uint32_t migration)
|
||||
{ migrationLogger << "Progress: Successfully migrated to: " << migration << Qt::endl; });
|
||||
migrator.performMigrations();
|
||||
migrationLogger << "Database upgraded successfully" << Qt::endl;
|
||||
}
|
||||
QVariant pdfViewer = settings.value(SETTINGS_KEY_PDFVIEWER);
|
||||
if(!pdfViewer.isValid())
|
||||
{
|
||||
setPdfViewer();
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,19 +165,18 @@ QString Common::ipcSocketPath()
|
||||
// return settings.value(SETTINGS_KEY_IPCSOCKETPATH, "/tmp/.looqs/looqs-ipc-socket").toString();
|
||||
}
|
||||
|
||||
static QStringList excludedPaths = {"/proc", "/sys", "/dev", "/tmp", "/var/run", "/run"};
|
||||
|
||||
QStringList Common::excludedPaths()
|
||||
{
|
||||
static int ran = false;
|
||||
static QStringList excludedPaths;
|
||||
if(!ran)
|
||||
{
|
||||
QSettings settings;
|
||||
QStringList userExcludedPaths = settings.value(SETTINGS_KEY_EXCLUDEDPATHS).toStringList();
|
||||
QStringList defaults{"/proc", "/sys", "/dev", "/tmp", "/var/run", "/run"};
|
||||
excludedPaths = settings.value(SETTINGS_KEY_EXCLUDEDPATHS, defaults).toStringList();
|
||||
ran = true;
|
||||
::excludedPaths.append(userExcludedPaths);
|
||||
}
|
||||
return ::excludedPaths;
|
||||
return excludedPaths;
|
||||
}
|
||||
|
||||
QStringList Common::mountPaths()
|
||||
|
@ -2,6 +2,14 @@
|
||||
#define COMMON_H
|
||||
#include <QCoreApplication>
|
||||
#include <QFileInfo>
|
||||
|
||||
#define SETTINGS_KEY_DBPATH "dbpath"
|
||||
#define SETTINGS_KEY_IPCSOCKETPATH "ipcsocketpath"
|
||||
#define SETTINGS_KEY_PDFVIEWER "pdfviewer"
|
||||
#define SETTINGS_KEY_EXCLUDEDPATHS "excludedpaths"
|
||||
#define SETTINGS_KEY_MOUNTPATHS "mountpaths"
|
||||
#define SETTINGS_KEY_PREVIEWSPERPAGE "previewsPerPage"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
void setupAppInfo();
|
||||
|
@ -19,7 +19,21 @@ void Indexer::beginIndexing()
|
||||
QVector<QString> dirs;
|
||||
|
||||
WildcardMatcher wildcardMatcher;
|
||||
wildcardMatcher.setPatterns(this->ignorePattern);
|
||||
|
||||
QStringList ignoreList = this->ignorePattern;
|
||||
|
||||
for(QString &excludedPath : Common::excludedPaths())
|
||||
{
|
||||
QString pattern = excludedPath;
|
||||
if(!pattern.endsWith("/"))
|
||||
{
|
||||
pattern += "/";
|
||||
}
|
||||
pattern += "*";
|
||||
ignoreList.append(excludedPath);
|
||||
}
|
||||
ignoreList.append(this->ignorePattern);
|
||||
wildcardMatcher.setPatterns(ignoreList);
|
||||
for(QString &path : this->pathsToScan)
|
||||
{
|
||||
if(wildcardMatcher.match(path))
|
||||
@ -40,7 +54,7 @@ void Indexer::beginIndexing()
|
||||
if(!dirs.empty())
|
||||
{
|
||||
this->dirScanner->setPaths(dirs);
|
||||
this->dirScanner->setIgnorePatterns(this->ignorePattern);
|
||||
this->dirScanner->setIgnorePatterns(ignoreList);
|
||||
|
||||
this->dirScanner->scan();
|
||||
}
|
||||
|
@ -23,12 +23,12 @@ QueryType LooqsQuery::getQueryType()
|
||||
return static_cast<QueryType>(tokensMask & COMBINED);
|
||||
}
|
||||
|
||||
bool LooqsQuery::hasContentSearch()
|
||||
bool LooqsQuery::hasContentSearch() const
|
||||
{
|
||||
return (this->getTokensMask() & FILTER_CONTENT) == FILTER_CONTENT;
|
||||
}
|
||||
|
||||
bool LooqsQuery::hasPathSearch()
|
||||
bool LooqsQuery::hasPathSearch() const
|
||||
{
|
||||
return (this->getTokensMask() & FILTER_PATH) == FILTER_PATH;
|
||||
}
|
||||
@ -181,8 +181,8 @@ LooqsQuery LooqsQuery::build(QString expression, TokenType loneWordsTokenType, b
|
||||
QStringList loneWords;
|
||||
LooqsQuery result;
|
||||
QRegularExpression rx(
|
||||
"((?<filtername>(\\.|\\w)+):(?<args>\\((?<innerargs>[^\\)]+)\\)|([\\p{L},])+)|(?<boolean>AND|OR)"
|
||||
"|(?<negation>!)|(?<bracket>\\(|\\))|(?<loneword>[\"\\p{L}]+))");
|
||||
"((?<filtername>(\\.|\\w)+):(?<args>\\((?<innerargs>[^\\)]+)\\)|([\\p{L}\\p{N},])+)|(?<boolean>AND|OR)"
|
||||
"|(?<negation>!)|(?<bracket>\\(|\\))|(?<loneword>[\"\\p{L}\\p{N}]+))");
|
||||
QRegularExpressionMatchIterator i = rx.globalMatch(expression);
|
||||
auto previousWasBool = [&result] { return !result.tokens.empty() && ((result.tokens.last().type & BOOL) == BOOL); };
|
||||
auto previousWas = [&result](TokenType t) { return !result.tokens.empty() && (result.tokens.last().type == t); };
|
||||
|
@ -61,8 +61,8 @@ class LooqsQuery
|
||||
{
|
||||
this->limit = limit;
|
||||
}
|
||||
bool hasContentSearch();
|
||||
bool hasPathSearch();
|
||||
bool hasContentSearch() const;
|
||||
bool hasPathSearch() const;
|
||||
|
||||
void addSortCondition(SortCondition sc);
|
||||
static bool checkParanthesis(QString query);
|
||||
|
@ -35,7 +35,7 @@ void SandboxedProcessor::enableSandbox(QString readablePath)
|
||||
struct exile_policy *policy = exile_init_policy();
|
||||
if(policy == NULL)
|
||||
{
|
||||
qCritical() << "Could not init exile";
|
||||
qCritical() << "Could not init exile" << Qt::endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
policy->namespace_options = EXILE_UNSHARE_NETWORK | EXILE_UNSHARE_USER;
|
||||
@ -43,6 +43,8 @@ void SandboxedProcessor::enableSandbox(QString readablePath)
|
||||
std::string readablePathLocation;
|
||||
if(!readablePath.isEmpty())
|
||||
{
|
||||
policy->namespace_options |= EXILE_UNSHARE_MOUNT;
|
||||
policy->mount_path_policies_to_chroot = 1;
|
||||
readablePathLocation = readablePath.toStdString();
|
||||
if(exile_append_path_policies(policy, EXILE_FS_ALLOW_ALL_READ, readablePathLocation.c_str()) != 0)
|
||||
{
|
||||
|
@ -7,6 +7,7 @@ class SearchResult
|
||||
public:
|
||||
FileData fileData;
|
||||
QVector<unsigned int> pages;
|
||||
bool wasContentSearch = false;
|
||||
};
|
||||
|
||||
#endif // SEARCHRESULT_H
|
||||
|
@ -213,6 +213,7 @@ QVector<SearchResult> SqliteSearch::search(const LooqsQuery &query)
|
||||
throw LooqsGeneralException("SQL Error: " + dbQuery.lastError().text());
|
||||
}
|
||||
|
||||
bool contentSearch = query.hasContentSearch();
|
||||
while(dbQuery.next())
|
||||
{
|
||||
SearchResult result;
|
||||
@ -229,6 +230,7 @@ QVector<SearchResult> SqliteSearch::search(const LooqsQuery &query)
|
||||
result.pages.append(page.toUInt());
|
||||
}
|
||||
}
|
||||
result.wasContentSearch = contentSearch;
|
||||
results.append(result);
|
||||
}
|
||||
return results;
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 42d44b0cc1e4ef35d0429e43a1dd005556450b44
|
||||
Subproject commit 8f38dc4480d51e2bf737ef87dd4a4f408d90a8a6
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren