Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .tx/transifex.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
filters:
- filter_type: file
source_file: tools/dde-login-reminder/translations/dde-login-reminder.ts
file_format: QT
source_language: en_US
translation_files_expression: tools/dde-login-reminder/translations/dde-login-reminder_<lang>.ts
settings:
pr_branch_name: transifex_update_<br_unique_id>
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ include(GNUInstallDirs)
set(QT_VERSION_MAJOR 6)
set(DTK_VERSION_MAJOR 6)

find_package(DtkBuildHelper REQUIRED)

macro(install_symlink filepath wantsdir)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/link/${wantsdir}/)
execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_INSTALL_PREFIX}/lib/systemd/user/${filepath} ${PROJECT_BINARY_DIR}/link/${wantsdir}/${filepath})
Expand Down
6 changes: 6 additions & 0 deletions lupdate.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash

# 更新 dde-login-reminder 翻译文件
lupdate tools/dde-login-reminder -ts -no-obsolete translations/dde-login-reminder.ts

# tx push -s -f --branch m20
6 changes: 6 additions & 0 deletions misc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ install(

install(FILES ${XSESSION} DESTINATION /etc/X11/Xsession.d/)
install(FILES ${PROFILE} DESTINATION /etc/profile.d)

# Install dconf configuration files
dtk_add_config_meta_files(
APPID org.deepin.dde.session
FILES ${CMAKE_CURRENT_SOURCE_DIR}/dconf/org.deepin.dde.session.json
)
17 changes: 17 additions & 0 deletions misc/dconf/org.deepin.dde.session.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"magic": "dsg.config.meta",
"version": "1.0",
"contents": {
"LoginReminder": {
"value": false,
"serial": 0,
"flags": [],
"name": "login reminder",
"name[zh_CN]": "登录提醒",
"description": "Display login reminder notification including login information, password expiration warning and failed login attempts",
"description[zh_CN]": "显示登录提醒通知,包括登录信息、密码过期警告和登录失败次数",
"permissions": "readwrite",
"visibility": "public"
}
}
}
23 changes: 17 additions & 6 deletions tools/dde-login-reminder/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt${QT_VERSION_MAJOR} COMPONENTS Core Network DBus REQUIRED)
find_package(Dtk${DTK_VERSION_MAJOR} COMPONENTS Core REQUIRED)

find_package(PkgConfig REQUIRED)

Expand All @@ -21,17 +22,27 @@ target_link_libraries(${BIN_NAME}
Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::DBus
Qt${QT_VERSION_MAJOR}::Network
Dtk${DTK_VERSION_MAJOR}::Core
)

target_include_directories(${BIN_NAME} PUBLIC
${PROJECT_SOURCE_DIR}/src/utils
)

target_compile_definitions(${BIN_NAME} PRIVATE
CMAKE_INSTALL_FULL_DATADIR="${CMAKE_INSTALL_FULL_DATADIR}"
)

install(TARGETS ${BIN_NAME} DESTINATION bin)

set(SYSTEMD_FILES
dde-login-reminder.service
)
install(FILES ${SYSTEMD_FILES} DESTINATION lib/systemd/user/)
install(DIRECTORY DESTINATION lib/systemd/user/dde-osd.target.wants/)
install_symlink(dde-login-reminder.service dde-osd.target.wants)
# Install desktop file to autostart directory
install(FILES dde-login-reminder.desktop DESTINATION /etc/xdg/autostart/)

# Translation files
file(GLOB TS_FILES ${CMAKE_CURRENT_SOURCE_DIR}/translations/*.ts)
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS LinguistTools)
if(Qt${QT_VERSION_MAJOR}LinguistTools_FOUND AND TS_FILES)
qt_add_translation(QM_FILES ${TS_FILES})
add_custom_target(${BIN_NAME}_translations ALL DEPENDS ${QM_FILES})
install(FILES ${QM_FILES} DESTINATION ${CMAKE_INSTALL_DATADIR}/${BIN_NAME}/translations)
endif()
10 changes: 10 additions & 0 deletions tools/dde-login-reminder/dde-login-reminder.desktop
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[Desktop Entry]
Type=Application
Name=DDE Login Reminder
Comment=Display login reminder notification
Exec=/usr/bin/dde-login-reminder
Icon=preferences-system
Terminal=false
Categories=System;
X-Deepin-AutoStart=true
NoDisplay=true
10 changes: 0 additions & 10 deletions tools/dde-login-reminder/dde-login-reminder.service

This file was deleted.

197 changes: 182 additions & 15 deletions tools/dde-login-reminder/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,45 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include <QCoreApplication>
#include <QDateTime>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QDBusPendingReply>
#include <QDBusServiceWatcher>
#include <QNetworkInterface>
#include <QTranslator>
#include <QLocale>
#include <QLibraryInfo>
#include <DConfig>

#include <unistd.h>

#include "utils.h"
#include "loginreminderinfo.h"

DCORE_USE_NAMESPACE

// 前置声明
int checkPasswordExpired();

int start()
{
qDebug() << "login reminder init";
#if 0
// TODO has no 'com.deepin.dde.startdde' gsetting
if (!Utils::SettingValue("com.deepin.dde.startdde", QByteArray(), "login-reminder", false).toBool())
return 0;
#endif
// 登录后展示横幅通知信息

// 读取 DConfig 配置,检查 LoginReminder 是否开启
DConfig *config = DConfig::create("org.deepin.dde.session", "org.deepin.dde.session");
bool loginReminderEnabled = false; // 默认关闭
if (config && config->isValid()) {
loginReminderEnabled = config->value("LoginReminder", false).toBool();
qDebug() << "LoginReminder enabled:" << loginReminderEnabled;
}

// 如果 login-reminder 没有开启,仅检查密码过期情况
if (!loginReminderEnabled) {
qDebug() << "LoginReminder is disabled, only checking password expiration";
return checkPasswordExpired();
}

QDBusInterface userInter("org.deepin.dde.Accounts1", QString("/org/deepin/dde/Accounts1/User%1").arg(getuid()), "org.deepin.dde.Accounts1.User", QDBusConnection::systemBus());
QDBusPendingReply<LoginReminderInfo> reply = userInter.call("GetReminderInfo");
if (reply.isError()) {
Expand Down Expand Up @@ -69,11 +88,18 @@ int start()
const QString &address = (info.CurrentLogin.Address == "0.0.0.0") ? getFirstIpAddress() : info.CurrentLogin.Address;
QString body = QString("%1 %2 %3").arg(info.Username).arg(address).arg(currentLoginTime);
int daysLeft = info.spent.LastChange + info.spent.Max - int(QDateTime::currentSecsSinceEpoch() / SECONDS_PER_DAY);
bool needNotify = false;
if ((info.spent.Max != -1) && (info.spent.Warn != -1) && (info.spent.Warn > daysLeft)) {
needNotify = true;
body += " " + QString(QObject::tr("Your password will expire in %1 days")).arg(daysLeft);
}
body += "\n" + QString(QObject::tr("%1 login failures since the last successful login")).arg(info.FailCountSinceLastLogin);

if (info.FailCountSinceLastLogin > 0) {
needNotify = true;
body += "\n" + QString(QObject::tr("%1 login failures since the last successful login")).arg(info.FailCountSinceLastLogin);
}
if (!needNotify) {
return 0;
}
const QString &lastLoginTime = info.LastLogin.Time.left(QString("yyyy-MM-dd hh:mm:ss").length());
QString content;
content += QString("<p>%1</p>").arg(info.Username);
Expand All @@ -95,7 +121,7 @@ int start()
uint notifyId = notifyIdReply.value();

// 5秒后自动关闭通知,避免在通知中心中显示
sleep(5);
sleep(10);

QDBusPendingReply<> closeReply = notifyInter->call("CloseNotification", notifyId);
if (closeReply.isError()) {
Expand All @@ -107,13 +133,154 @@ int start()
return 0;
}

int checkPasswordExpired() {
qDebug() << "check password expired";

// 调用 PasswordExpiredInfo 获取密码过期信息
QDBusInterface userInter("org.deepin.dde.Accounts1",
QString("/org/deepin/dde/Accounts1/User%1").arg(getuid()),
"org.deepin.dde.Accounts1.User",
QDBusConnection::systemBus());

QDBusMessage reply = userInter.call("PasswordExpiredInfo");
if (reply.type() == QDBusMessage::ErrorMessage) {
qWarning() << "failed to retrieve password expired info, error: " << reply.errorMessage();
return -1;
}

// 解析返回值: (Int32 expiredStatus, Int64 dayLeft)
QList<QVariant> args = reply.arguments();
if (args.size() < 2) {
qWarning() << "invalid reply from PasswordExpiredInfo";
return -1;
}

int expiredStatus = args[0].toInt();
qint64 dayLeft = args[1].toLongLong();

qDebug() << "Password expired status:" << expiredStatus << ", days left:" << dayLeft;

// expiredStatus: 0=正常, 1=即将过期, 2=已过期
if (expiredStatus == 0) {
qDebug() << "Password is valid, no notification needed";
return 0;
}

// 构建通知内容
QString title = QObject::tr("Password Expiration Warning");
QString body;
QString content;

if (expiredStatus == 2) {
// 密码已过期
body = QObject::tr("Your password has expired. Please change it immediately.");
content = QString("<p><b>%1</b></p>").arg(QObject::tr("Your password has expired!"));
content += QString("<p>%1</p>").arg(QObject::tr("For security reasons, please change your password immediately."));
} else if (expiredStatus == 1 && dayLeft > 0) {
// 密码即将过期
body = QObject::tr("Your password will expire in %1 days. Please change it soon.").arg(dayLeft);
content = QString("<p><b>%1</b></p>").arg(QObject::tr("Password Expiration Warning"));
content += QString("<p>%1</p>").arg(QObject::tr("Your password will expire in %1 days.").arg(dayLeft));
content += QString("<p>%1</p>").arg(QObject::tr("Please change your password as soon as possible."));
} else {
// 其他情况
qDebug() << "Unknown password status, no notification";
return 0;
}

// 发送通知
QDBusInterface *notifyInter = new QDBusInterface("org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications");
QVariantMap hints;
// 点击通知后打开控制中心的账户密码修改页面
hints.insert(QString("x-deepin-action-change_password"),
QVariant(QString("dbus-send,--print-reply,--dest=org.deepin.dde.ControlCenter1,/org/deepin/dde/ControlCenter1,org.deepin.dde.ControlCenter1.ShowPage,string:accountsloginMethodItempassword")));

QDBusPendingReply<uint> notifyIdReply = notifyInter->call("Notify",
"dde-control-center",
uint(0),
"preferences-system",
title,
body,
QStringList() << "change_password" << QObject::tr("Change Password"),
hints,
int(0));
if (notifyIdReply.isError()) {
qWarning() << "failed to call notification, error: " << notifyIdReply.error().message();
return -1;
}
uint notifyId = notifyIdReply.value();

// 10秒后自动关闭通知
sleep(5);

QDBusPendingReply<> closeReply = notifyInter->call("CloseNotification", notifyId);
if (closeReply.isError()) {
qWarning() << "failed to close notification, error: " << closeReply.error().message();
return -1;
}

qDebug() << "password expired check finished";
return 0;
}

int main(int argc, char *argv[])
{
Q_UNUSED(argc);
Q_UNUSED(argv);
QCoreApplication app(argc, argv);
app.setOrganizationName("deepin");
app.setApplicationName("dde-login-reminder");

// 加载翻译文件
QTranslator *translator = new QTranslator(&app);
QString locale = QLocale::system().name();

// 尝试多个可能的翻译路径
QStringList translationPaths;
translationPaths << QString(CMAKE_INSTALL_FULL_DATADIR) + "/dde-login-reminder/translations"
<< "/usr/share/dde-login-reminder/translations"
<< "/usr/local/share/dde-login-reminder/translations";

bool loaded = false;
for (const QString &path : translationPaths) {
if (translator->load(QString("dde-login-reminder_%1").arg(locale), path)) {
app.installTranslator(translator);
qDebug() << "Loaded translation for locale:" << locale << "from" << path;
loaded = true;
break;
}
}

if (!loaded) {
qDebug() << "Failed to load translation for locale:" << locale;
qDebug() << "Tried paths:" << translationPaths;
}

registerLoginReminderInfoMetaType();

return 0;
// return start();
}
// 检查 org.deepin.dde.Notification1 服务是否已启动
QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface();
if (interface->isServiceRegistered("org.deepin.dde.Notification1")) {
// 服务已启动,直接执行
qDebug() << "Notification service already available, starting immediately";
int result = start();
return result;
}

// 服务未启动,动态监听服务注册
qDebug() << "Notification service not available, waiting for it to start...";
QDBusServiceWatcher *watcher = new QDBusServiceWatcher(
"org.deepin.dde.Notification1",
QDBusConnection::sessionBus(),
QDBusServiceWatcher::WatchForRegistration,
&app
);

QObject::connect(watcher, &QDBusServiceWatcher::serviceRegistered, [&app](const QString &serviceName) {
qDebug() << "Notification service registered:" << serviceName;
int result = start();
app.exit(result);
});

return app.exec();
}
Loading
Loading