2022-04-24 14:52:13 +08:00
|
|
|
|
/*
|
2022-05-15 12:10:42 +08:00
|
|
|
|
* Copyright (C) 2021 ~ 2022 Deepin Technology Co., Ltd.
|
2022-04-24 14:52:13 +08:00
|
|
|
|
*
|
|
|
|
|
* Author: weizhixiang <weizhixiang@uniontech.com>
|
|
|
|
|
*
|
|
|
|
|
* Maintainer: weizhixiang <weizhixiang@uniontech.com>
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
* 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 "windowpatterns.h"
|
|
|
|
|
#include "common.h"
|
2022-05-27 21:03:41 +08:00
|
|
|
|
#include "processinfo.h"
|
|
|
|
|
#include "dfile.h"
|
|
|
|
|
#include "dstring.h"
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|
|
|
|
|
#include <QJsonDocument>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
#include <QJsonValue>
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QVariant>
|
|
|
|
|
#include <QVariantMap>
|
|
|
|
|
#include <QDebug>
|
|
|
|
|
#include <dstring.h>
|
|
|
|
|
#include <QRegExp>
|
|
|
|
|
#include <QFile>
|
|
|
|
|
|
|
|
|
|
const int parsedFlagNegative = 0x001;
|
|
|
|
|
const int parsedFlagIgnoreCase = 0x010;
|
|
|
|
|
|
|
|
|
|
bool contains(QString key, QString value) {
|
|
|
|
|
return key.contains(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool containsIgnoreCase(QString key, QString value) {
|
|
|
|
|
QString _key = key.toLower();
|
|
|
|
|
QString _value = value.toLower();
|
|
|
|
|
return _key.contains(_value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool equal(QString key, QString value) {
|
|
|
|
|
return key == value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool equalIgnoreCase(QString key, QString value) {
|
|
|
|
|
return key.toLower() == value.toLower();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool regexMatch(QString key, QString value) {
|
|
|
|
|
QRegExp ruleRegex(value);
|
2022-05-27 21:03:41 +08:00
|
|
|
|
bool ret = ruleRegex.exactMatch(key);
|
|
|
|
|
|
|
|
|
|
// 配置中\.exe$ 在V20中go代码可以匹配以.exe结尾的字符串, Qt中使用\.*exe$匹配以.exe结尾字符串失败,暂时做兼容处理
|
|
|
|
|
if (!ret && value == "\\.exe$") {
|
|
|
|
|
ret = key.endsWith(".exe");
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool regexMatchIgnoreCase(QString key, QString value) {
|
|
|
|
|
QRegExp ruleRegex(value, Qt::CaseInsensitive);
|
2022-05-27 21:03:41 +08:00
|
|
|
|
bool ret = ruleRegex.exactMatch(key);
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|
2022-05-27 21:03:41 +08:00
|
|
|
|
// 配置中\.exe$ 在V20中go代码可以匹配以.exe结尾的字符串, Qt中使用\.*exe$匹配以.exe结尾字符串失败,暂时做兼容处理
|
|
|
|
|
if (!ret && value == "\\.exe$") {
|
|
|
|
|
QString _key = key.toLower();
|
|
|
|
|
ret = _key.endsWith(".exe");
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|
|
|
|
|
RuleValueParse::RuleValueParse()
|
2022-05-27 21:03:41 +08:00
|
|
|
|
: negative(false)
|
2022-04-24 14:52:13 +08:00
|
|
|
|
, type(0)
|
|
|
|
|
, flags(0)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 21:03:41 +08:00
|
|
|
|
bool RuleValueParse::match(const WindowInfoX *winInfo)
|
2022-04-24 14:52:13 +08:00
|
|
|
|
{
|
2022-05-27 21:03:41 +08:00
|
|
|
|
QString parsedKey = parseRuleKey(const_cast<WindowInfoX *>(winInfo), key);
|
2022-04-24 14:52:13 +08:00
|
|
|
|
if (!fn)
|
|
|
|
|
return false;
|
|
|
|
|
|
2022-05-27 21:03:41 +08:00
|
|
|
|
bool ret = fn(parsedKey, value);
|
|
|
|
|
return negative ? !ret : ret;
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 21:03:41 +08:00
|
|
|
|
QString RuleValueParse::parseRuleKey(WindowInfoX *winInfo, const QString &ruleKey)
|
2022-04-24 14:52:13 +08:00
|
|
|
|
{
|
2022-05-27 21:03:41 +08:00
|
|
|
|
ProcessInfo * process = winInfo->getProcess();
|
|
|
|
|
if (ruleKey == "hasPid") {
|
|
|
|
|
if (process && process->initWithPid()) {
|
|
|
|
|
return "t";
|
|
|
|
|
}
|
|
|
|
|
return "f";
|
|
|
|
|
} else if (ruleKey == "exec") {
|
|
|
|
|
if (process) {
|
|
|
|
|
// 返回执行文件baseName
|
|
|
|
|
auto baseName = DFile::base(process->getExe());
|
|
|
|
|
return baseName.empty() ? "" : baseName.c_str();
|
|
|
|
|
}
|
|
|
|
|
} else if (ruleKey == "arg") {
|
|
|
|
|
if (process) {
|
|
|
|
|
// 返回命令行参数
|
|
|
|
|
auto ret = DString::join(process->getArgs(), "");
|
|
|
|
|
return ret.empty() ? "" : ret.c_str();
|
|
|
|
|
}
|
|
|
|
|
} else if (ruleKey == "wmi") {
|
|
|
|
|
// 窗口实例
|
|
|
|
|
auto wmClass = winInfo->getWMClass();
|
|
|
|
|
if (!wmClass.instanceName.empty())
|
|
|
|
|
return wmClass.instanceName.c_str();
|
|
|
|
|
} else if (ruleKey == "wmc") {
|
|
|
|
|
// 窗口类型
|
|
|
|
|
auto wmClass = winInfo->getWMClass();
|
|
|
|
|
if (!wmClass.className.empty())
|
|
|
|
|
return wmClass.className.c_str();
|
|
|
|
|
} else if (ruleKey == "wmn") {
|
|
|
|
|
// 窗口名称
|
|
|
|
|
return winInfo->getWMName();
|
|
|
|
|
} else if (ruleKey == "wmrole") {
|
|
|
|
|
// 窗口角色
|
|
|
|
|
return winInfo->getWmRole();
|
|
|
|
|
} else {
|
|
|
|
|
const QString envPrefix = "env.";
|
|
|
|
|
if (ruleKey.startsWith(envPrefix)) {
|
|
|
|
|
QString envName = ruleKey.mid(envPrefix.size());
|
|
|
|
|
if (winInfo->getProcess()) {
|
|
|
|
|
auto ret = process->getEnv(envName.toStdString());
|
|
|
|
|
return ret.empty() ? "" : ret.c_str();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|
2022-05-27 21:03:41 +08:00
|
|
|
|
return "";
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
WindowPatterns::WindowPatterns()
|
|
|
|
|
{
|
2022-05-27 21:03:41 +08:00
|
|
|
|
loadWindowPatterns();
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-26 18:10:53 +08:00
|
|
|
|
/**
|
2022-05-27 21:03:41 +08:00
|
|
|
|
* @brief WindowPatterns::match 匹配窗口类型
|
2022-05-26 18:10:53 +08:00
|
|
|
|
* @param winInfo
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2022-04-24 14:52:13 +08:00
|
|
|
|
QString WindowPatterns::match(WindowInfoX *winInfo)
|
|
|
|
|
{
|
2022-05-26 18:10:53 +08:00
|
|
|
|
for (auto pattern : patterns) {
|
2022-05-27 21:03:41 +08:00
|
|
|
|
bool patternOk = true;
|
|
|
|
|
for (auto rule : pattern.parseRules) {
|
|
|
|
|
if (!rule.match(winInfo)) {
|
|
|
|
|
patternOk = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-24 14:52:13 +08:00
|
|
|
|
|
2022-05-27 21:03:41 +08:00
|
|
|
|
if (patternOk) {
|
|
|
|
|
// 匹配成功
|
|
|
|
|
return pattern.result;
|
|
|
|
|
}
|
2022-05-26 18:10:53 +08:00
|
|
|
|
}
|
2022-05-27 21:03:41 +08:00
|
|
|
|
|
|
|
|
|
// 匹配失败
|
2022-04-24 14:52:13 +08:00
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void WindowPatterns::loadWindowPatterns()
|
|
|
|
|
{
|
|
|
|
|
qInfo() << "---loadWindowPatterns";
|
|
|
|
|
QFile file(windowPatternsFile);
|
|
|
|
|
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
|
|
|
|
|
file.close();
|
|
|
|
|
if (!doc.isArray())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QJsonArray arr = doc.array();
|
|
|
|
|
if (arr.size() == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
patterns.clear();
|
|
|
|
|
for (auto iterp = arr.begin(); iterp != arr.end(); iterp++) {
|
|
|
|
|
// 过滤非Object
|
|
|
|
|
if (!(*iterp).isObject())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
QJsonObject patternObj = (*iterp).toObject();
|
|
|
|
|
QVariantMap patternMap = patternObj.toVariantMap();
|
|
|
|
|
WindowPattern pattern;
|
|
|
|
|
for (auto infoIter = patternMap.begin(); infoIter != patternMap.end(); infoIter++) {
|
|
|
|
|
QString ret = infoIter.key();
|
|
|
|
|
QVariant value = infoIter.value();
|
|
|
|
|
|
|
|
|
|
if (ret == "ret") {
|
|
|
|
|
pattern.result = value.toString();
|
|
|
|
|
} else if (ret == "rules") {
|
|
|
|
|
for (auto &item : value.toList()) {
|
|
|
|
|
if (!item.isValid())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (item.toList().size() != 2)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
pattern.rules.push_back({item.toList()[0].toString(), item.toList()[1].toString()});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
qInfo() << pattern.result;
|
|
|
|
|
for (const auto &item : pattern.rules) {
|
|
|
|
|
qInfo() << item[0] << " " << item[1];
|
|
|
|
|
}
|
|
|
|
|
patterns.push_back(pattern);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解析patterns
|
|
|
|
|
for (auto &pattern : patterns) {
|
|
|
|
|
for (int i=0; i < pattern.rules.size(); i++) {
|
|
|
|
|
RuleValueParse ruleValue = parseRule(pattern.rules[i]);
|
|
|
|
|
pattern.parseRules.push_back(ruleValue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// "=:XXX" equal XXX
|
|
|
|
|
// "=!XXX" not equal XXX
|
|
|
|
|
|
|
|
|
|
// "c:XXX" contains XXX
|
|
|
|
|
// "c!XXX" not contains XXX
|
|
|
|
|
|
|
|
|
|
// "r:XXX" match regexp XXX
|
|
|
|
|
// "r!XXX" not match regexp XXX
|
|
|
|
|
|
|
|
|
|
// e c r ignore case
|
|
|
|
|
// = E C R not ignore case
|
|
|
|
|
// 解析窗口类型规则
|
|
|
|
|
RuleValueParse WindowPatterns::parseRule(QVector<QString> rule)
|
|
|
|
|
{
|
|
|
|
|
RuleValueParse ret;
|
|
|
|
|
ret.key = rule[0];
|
|
|
|
|
ret.original = rule[1];
|
|
|
|
|
if (rule[1].size() < 2)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
int len = ret.original.size() + 1;
|
|
|
|
|
char *orig = static_cast<char *>(calloc(1, size_t(len)));
|
|
|
|
|
if (!orig)
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
|
|
strncpy(orig, ret.original.toStdString().c_str(), size_t(len));
|
|
|
|
|
switch (orig[1]) {
|
|
|
|
|
case ':':
|
2022-05-27 21:03:41 +08:00
|
|
|
|
break;
|
2022-04-24 14:52:13 +08:00
|
|
|
|
case '!':
|
|
|
|
|
ret.flags |= parsedFlagNegative;
|
|
|
|
|
ret.negative = true;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret.value = QString(&orig[2]);
|
|
|
|
|
ret.type = uint8_t(orig[0]);
|
|
|
|
|
switch (orig[0]) {
|
|
|
|
|
case 'C':
|
|
|
|
|
ret.fn = contains;
|
|
|
|
|
break;
|
|
|
|
|
case 'c':
|
|
|
|
|
ret.flags |= parsedFlagIgnoreCase;
|
|
|
|
|
ret.fn = containsIgnoreCase;
|
|
|
|
|
break;
|
|
|
|
|
case '=':
|
|
|
|
|
case 'E':
|
|
|
|
|
ret.fn = equal;
|
|
|
|
|
break;
|
|
|
|
|
case 'e':
|
|
|
|
|
ret.flags |= parsedFlagIgnoreCase;
|
|
|
|
|
ret.fn = equalIgnoreCase;
|
|
|
|
|
break;
|
|
|
|
|
case 'R':
|
|
|
|
|
ret.fn = regexMatch;
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
ret.flags |= parsedFlagIgnoreCase;
|
|
|
|
|
ret.fn = regexMatchIgnoreCase;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2022-05-27 21:03:41 +08:00
|
|
|
|
break;
|
2022-04-24 14:52:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
2022-05-27 21:03:41 +08:00
|
|
|
|
free(orig);
|
2022-04-24 14:52:13 +08:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|