Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions libs/linglong/src/linglong/cli/cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include "linglong/utils/namespace.h"
#include "linglong/utils/runtime_config.h"
#include "linglong/utils/xdg/directory.h"
#include "linglong/utils/xdp.h"
#include "ocppi/runtime/ExecOption.hpp"
#include "ocppi/runtime/RunOption.hpp"
#include "ocppi/runtime/Signal.hpp"
Expand Down Expand Up @@ -867,6 +868,14 @@ int Cli::runResolvedContext(runtime::RunContext &runContext,
return -1;
}

const auto &appid = runContext.getTargetID();
if (!options.disableXdp.has_value() && !utils::isValidXdgDesktopPortalId(appid)) {
LogW("appid '{}' doesn't conform to XDP ID specification, disabling XDP integration. "
"Use --enable-xdp to override.",
appid);
runOptions.disableXdp = true;
}

auto container = this->containerBuilder.createRunContainer(runContext, runOptions);
if (!container) {
this->printer.printErr(container.error());
Expand Down
1 change: 1 addition & 0 deletions libs/linglong/tests/ll-tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pfl_add_executable(
src/linglong/utils/sha256_test.cpp
src/linglong/utils/transaction_test.cpp
src/linglong/utils/xdg/directory_test.cpp
src/linglong/utils/xdp_test.cpp
src/main.cpp


Expand Down
101 changes: 101 additions & 0 deletions libs/linglong/tests/ll-tests/src/linglong/utils/xdp_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

#include <gtest/gtest.h>

#include "linglong/utils/xdp.h"

using namespace linglong::utils;

TEST(IsValidXdgDesktopPortalIdTest, ValidStandardId)
{
EXPECT_TRUE(isValidXdgDesktopPortalId("org.example.app"));
EXPECT_TRUE(isValidXdgDesktopPortalId("com.deepin.calculator"));
EXPECT_TRUE(isValidXdgDesktopPortalId("org.freedesktop.portal"));
}

TEST(IsValidXdgDesktopPortalIdTest, ValidWithHyphenInLastSegment)
{
EXPECT_TRUE(isValidXdgDesktopPortalId("org.example.my-app"));
EXPECT_TRUE(isValidXdgDesktopPortalId("com.deepin.app-launcher"));
}

TEST(IsValidXdgDesktopPortalIdTest, ValidWithUnderscore)
{
EXPECT_TRUE(isValidXdgDesktopPortalId("org.example_my.app"));
EXPECT_TRUE(isValidXdgDesktopPortalId("com.deepin.my_app"));
}

TEST(IsValidXdgDesktopPortalIdTest, ValidWithDigits)
{
EXPECT_TRUE(isValidXdgDesktopPortalId("org.example.app2"));
EXPECT_TRUE(isValidXdgDesktopPortalId("org.2example.app"));
}

TEST(IsValidXdgDesktopPortalIdTest, ValidMultiSegment)
{
EXPECT_TRUE(isValidXdgDesktopPortalId("org.deepin.linglong.app"));
EXPECT_TRUE(isValidXdgDesktopPortalId("a.b.c.d.e"));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidSingleSegment)
{
EXPECT_FALSE(isValidXdgDesktopPortalId("single"));
EXPECT_FALSE(isValidXdgDesktopPortalId("app"));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidEmptyString)
{
EXPECT_FALSE(isValidXdgDesktopPortalId(""));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidHyphenInNonLastSegment)
{
EXPECT_FALSE(isValidXdgDesktopPortalId("org.my-app.app"));
EXPECT_FALSE(isValidXdgDesktopPortalId("my-org.example.app"));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidEmptySegment)
{
EXPECT_FALSE(isValidXdgDesktopPortalId("org..app"));
EXPECT_FALSE(isValidXdgDesktopPortalId(".org.app"));
EXPECT_FALSE(isValidXdgDesktopPortalId("org.app."));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidSpecialChars)
{
EXPECT_FALSE(isValidXdgDesktopPortalId("org.example.app!"));
EXPECT_FALSE(isValidXdgDesktopPortalId("org.example.app@home"));
EXPECT_FALSE(isValidXdgDesktopPortalId("org/example/app"));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidSlashInId)
{
EXPECT_FALSE(isValidXdgDesktopPortalId("org.deepin.calculator/stable"));
EXPECT_FALSE(isValidXdgDesktopPortalId("org/deepin/app"));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidHyphenInMultipleNonLastSegments)
{
EXPECT_FALSE(isValidXdgDesktopPortalId("my-org.my-app.app"));
}

TEST(IsValidXdgDesktopPortalIdTest, ValidHyphenOnlyInLastSegment)
{
EXPECT_TRUE(isValidXdgDesktopPortalId("org.example.a-b"));
EXPECT_TRUE(isValidXdgDesktopPortalId("org.example.a-b-c"));
}

TEST(IsValidXdgDesktopPortalIdTest, InvalidLastSegmentEmpty)
{
EXPECT_FALSE(isValidXdgDesktopPortalId("org.example."));
}

TEST(IsValidXdgDesktopPortalIdTest, TwoSegmentsValid)
{
EXPECT_TRUE(isValidXdgDesktopPortalId("org.app"));
EXPECT_TRUE(isValidXdgDesktopPortalId("a.b"));
}
1 change: 1 addition & 0 deletions libs/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ pfl_add_library(
src/linglong/utils/transaction.h
src/linglong/utils/xdg/directory.cpp
src/linglong/utils/xdg/directory.h
src/linglong/utils/xdp.h
COMPILE_FEATURES
PUBLIC
cxx_std_17
Expand Down
53 changes: 53 additions & 0 deletions libs/utils/src/linglong/utils/xdp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
*
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

#pragma once

#include <algorithm>
#include <cctype>
#include <string>
#include <vector>

namespace linglong::utils {

inline bool isValidXdgDesktopPortalId(std::string_view appid) noexcept
{
std::vector<std::string_view> segments;

size_t start = 0;
for (size_t i = 0; i < appid.size(); ++i) {
if (appid[i] == '.') {
segments.push_back(appid.substr(start, i - start));
start = i + 1;
}
}
segments.push_back(appid.substr(start));

if (segments.size() < 2) {
return false;
}

for (size_t i = 0; i + 1 < segments.size(); ++i) {
if (segments[i].empty()) {
return false;
}
if (!std::all_of(segments[i].begin(), segments[i].end(), [](char c) {
return std::isalnum(static_cast<unsigned char>(c)) || c == '_';
})) {
Comment thread
reddevillg marked this conversation as resolved.
return false;
}
}

const auto &last = segments.back();
if (last.empty()) {
return false;
}
return std::all_of(last.begin(), last.end(), [](char c) {
return std::isalnum(static_cast<unsigned char>(c)) || c == '_' || c == '-';
});
Comment thread
reddevillg marked this conversation as resolved.
}

} // namespace linglong::utils
Loading