Command line to run upon incoming call

This commit is contained in:
Christophe Deschamps 2025-04-09 14:32:00 +01:00
parent d682cf1b8a
commit 0f435d32af
10 changed files with 126 additions and 1 deletions

View file

@ -122,6 +122,8 @@ SettingsCore::SettingsCore(QObject *parent) : QObject(parent) {
INIT_CORE_MEMBER(ShortcutCount, settingsModel)
INIT_CORE_MEMBER(Shortcuts, settingsModel)
INIT_CORE_MEMBER(CallToneIndicationsEnabled, settingsModel)
INIT_CORE_MEMBER(CommandLine, settingsModel)
INIT_CORE_MEMBER(DisableCommandLine, settingsModel)
}
SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
@ -194,6 +196,8 @@ SettingsCore::SettingsCore(const SettingsCore &settingsCore) {
mShortcutCount = settingsCore.mShortcutCount;
mShortcuts = settingsCore.mShortcuts;
mCallToneIndicationsEnabled = settingsCore.mCallToneIndicationsEnabled;
mCommandLine = settingsCore.mCommandLine;
mDisableCommandLine = settingsCore.mDisableCommandLine;
mDefaultDomain = settingsCore.mDefaultDomain;
mShowAccountDevices = settingsCore.mShowAccountDevices;
@ -409,6 +413,10 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
shortcuts, Shortcuts)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
callToneIndicationsEnabled, CallToneIndicationsEnabled)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, QString,
commandLine, CommandLine)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
disableCommandLine, DisableCommandLine)
auto coreModelConnection = SafeConnection<SettingsCore, CoreModel>::create(me, CoreModel::getInstance());

View file

@ -229,6 +229,8 @@ public:
DECLARE_CORE_MEMBER(int, shortcutCount, ShortcutCount)
DECLARE_CORE_MEMBER(QVariantList, shortcuts, Shortcuts)
DECLARE_CORE_GETSET_MEMBER(bool, callToneIndicationsEnabled, CallToneIndicationsEnabled)
DECLARE_CORE_GETSET_MEMBER(bool, disableCommandLine, DisableCommandLine)
DECLARE_CORE_GETSET_MEMBER(QString, commandLine, CommandLine)
signals:

View file

@ -1101,6 +1101,18 @@
<extracomment>&quot;Autoriser la vidéo&quot;</extracomment>
<translation>Enable video</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/CallSettingsLayout.qml" line="77"/>
<source>settings_calls_command_line_title</source>
<extracomment>&quot;Redirection vers outil externe lors d'un appel entrant&quot;</extracomment>
<translation>Command Line to run upon incoming call</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/CallSettingsLayout.qml" line="78"/>
<source>settings_calls_command_line_title_place_holder</source>
<extracomment>&quot;commande "https://exemple.com/?numero=$1&amp;nom=$2"&quot;</extracomment>
<translation>command "https://example.com/?phone=$1&amp;displayName=$2"</translation>
</message>
</context>
<context>
<name>CallStatistics</name>

View file

@ -1101,6 +1101,18 @@
<extracomment>&quot;Autoriser la vidéo&quot;</extracomment>
<translation>Autoriser la vidéo</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/CallSettingsLayout.qml" line="77"/>
<source>settings_calls_command_line_title</source>
<extracomment>&quot;Redirection vers outil externe lors d'un appel entrant&quot;</extracomment>
<translation>Redirection vers outil externe lors d'un appel entrant.</translation>
</message>
<message>
<location filename="../../view/Page/Layout/Settings/CallSettingsLayout.qml" line="78"/>
<source>settings_calls_command_line_title_place_holder</source>
<extracomment>&quot;commande "https://exemple.com/?numero=$1&amp;nom=$2"&quot;</extracomment>
<translation>commande "https://exemple.com/?numero=$1&amp;nom=$2"</translation>
</message>
</context>
<context>
<name>CallStatistics</name>

View file

@ -410,6 +410,16 @@ void CoreModel::onCallStateChanged(const std::shared_ptr<linphone::Core> &core,
const std::string &message) {
if (state == linphone::Call::State::IncomingReceived) {
App::getInstance()->getNotifier()->notifyReceivedCall(call);
if (!core->getConfig()->getBool(SettingsModel::UiSection, "disable_command_line", false) &&
!core->getConfig()->getString(SettingsModel::UiSection, "command_line", "").empty()) {
QString command = Utils::coreStringToAppString(
core->getConfig()->getString(SettingsModel::UiSection, "command_line", ""));
QString userName = Utils::coreStringToAppString(call->getRemoteAddress()->getUsername());
QString displayName = Utils::coreStringToAppString(call->getRemoteAddress()->getDisplayName());
command = command.replace("$1", userName);
command = command.replace("$2", displayName);
Utils::runCommandLine(command);
}
}
if (state == linphone::Call::State::End && SettingsModel::dndEnabled(core->getConfig()) &&
core->getCallsNb() == 0) { // Disable tones in DND mode if no more calls are running.

View file

@ -732,6 +732,8 @@ void SettingsModel::notifyConfigReady(){
DEFINE_NOTIFY_CONFIG_READY(shortcuts, Shortcuts)
DEFINE_NOTIFY_CONFIG_READY(usernameOnlyForLdapLookupsInCalls, UsernameOnlyForLdapLookupsInCalls)
DEFINE_NOTIFY_CONFIG_READY(usernameOnlyForCardDAVLookupsInCalls, UsernameOnlyForCardDAVLookupsInCalls)
DEFINE_NOTIFY_CONFIG_READY(commandLine, CommandLine)
DEFINE_NOTIFY_CONFIG_READY(disableCommandLine, DisableCommandLine)
}
DEFINE_GETSET_CONFIG(SettingsModel, bool, Bool, disableChatFeature, DisableChatFeature, "disable_chat_feature", true)
@ -860,4 +862,17 @@ DEFINE_GETSET_CONFIG(SettingsModel,
UsernameOnlyForCardDAVLookupsInCalls,
"username_only_for_carddav_lookups_in_calls",
false)
DEFINE_GETSET_CONFIG_STRING(SettingsModel,
commandLine,
CommandLine,
"command_line",
"")
DEFINE_GETSET_CONFIG(SettingsModel,
bool,
Bool,
disableCommandLine,
DisableCommandLine,
"disable_command_line",
false)
// clang-format on

View file

@ -184,6 +184,8 @@ public:
DECLARE_GETSET(QVariantList, shortcuts, Shortcuts)
DECLARE_GETSET(bool, usernameOnlyForLdapLookupsInCalls, UsernameOnlyForLdapLookupsInCalls)
DECLARE_GETSET(bool, usernameOnlyForCardDAVLookupsInCalls, UsernameOnlyForCardDAVLookupsInCalls)
DECLARE_GETSET(QString, commandLine, CommandLine)
DECLARE_GETSET(bool, disableCommandLine, DisableCommandLine)
signals:
void logsUploadUrlChanged();

View file

@ -40,10 +40,18 @@
#include <QDesktopServices>
#include <QHostAddress>
#include <QImageReader>
#include <QProcess>
#include <QQuickWindow>
#include <QRandomGenerator>
#include <QRegularExpression>
#ifdef Q_OS_WIN
#ifndef NOMINMAX
#define NOMINMAX 1
#endif
#include <windows.h>
#endif
DEFINE_ABSTRACT_OBJECT(Utils)
// =============================================================================
@ -1440,7 +1448,7 @@ QList<QVariant> Utils::append(const QList<QVariant> a, const QList<QVariant> b)
QString Utils::getAddressToDisplay(QVariantList addressList, QString filter, QString defaultAddress) {
if (filter.isEmpty()) return defaultAddress;
for (auto& item: addressList) {
for (auto &item : addressList) {
QString address = item.toMap()["address"].toString();
if (address.contains(filter)) return address;
}
@ -1497,3 +1505,44 @@ Utils::createFriendDeviceVariant(const QString &name, const QString &address, Li
map.insert("securityLevel", QVariant::fromValue(level));
return map;
}
// CLI
void Utils::runCommandLine(const QString command) {
QStringList arguments;
QString program;
#ifdef Q_OS_WIN
std::wstring fullCommand = std::wstring(L"cmd.exe /C ") + command.toStdWString();
STARTUPINFOW si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
BOOL success = CreateProcessW(nullptr, // Application name
fullCommand.data(), // Command line (mutable)
nullptr, // Process security attributes
nullptr, // Primary thread security attributes
FALSE, // Inherit handles
CREATE_NO_WINDOW, // Creation flags (hide window)
nullptr, // Environment
nullptr, // Current directory
&si, // STARTUPINFO
&pi // PROCESS_INFORMATION
);
if (success) {
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
lWarning() << "Failed to start process. GetLastError() =" << (int)GetLastError();
}
#elif defined(Q_OS_MACOS) || defined(Q_OS_LINUX)
QProcess::startDetached("/bin/sh", {"-c", command});
#else
lWarning() << "Unsupported OS!";
#endif
}

View file

@ -186,6 +186,10 @@ public:
static QVariantMap
createFriendDeviceVariant(const QString &name, const QString &address, LinphoneEnums::SecurityLevel level);
// CLI
static void runCommandLine(QString command);
private:
DECLARE_ABSTRACT_OBJECT
};

View file

@ -68,6 +68,17 @@ AbstractSettingsLayout {
propertyName: "videoEnabled"
propertyOwner: SettingsCpp
}
DecoratedTextField {
visible: !SettingsCpp.disableCommandLine
Layout.fillWidth: true
propertyName: "commandLine"
propertyOwner: SettingsCpp
//: Command line
title: qsTr("settings_calls_command_line_title")
placeHolder: qsTr("settings_calls_command_line_title_place_holder")
useTitleAsPlaceHolder: false
toValidate: true
}
}
}