This commit is contained in:
cocopaw 2025-08-18 00:06:12 +03:00 committed by GitHub
commit 6b9fa8f683
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 59 additions and 1 deletions

View file

@ -273,6 +273,56 @@ int TorrentContentModel::columnCount([[maybe_unused]] const QModelIndex &parent)
return TorrentContentModelItem::NB_COL; return TorrentContentModelItem::NB_COL;
} }
// Validates a file name, where "file" refers to both files and directories in Windows and Unix-like systems.
// Rejects empty or special names (".", ".."), platform-specific lengths or reserved names, and forbidden characters.
bool TorrentContentModel::isInvalidName(const QString &name) const
{
// Reject empty names or special directory names (".", "..")
if (name.isEmpty() || name == u"."_s || name == u".."_s)
return true;
#ifdef Q_OS_WIN
// Windows restricts file names to 255 characters and prohibits trailing dots
if (name.length() > 255 || name.endsWith(u'.'))
return true;
#else
// Non-Windows systems limit file name lengths to 255 bytes in UTF-8 encoding
if (name.toUtf8().length() > 255)
return true;
#endif
#ifdef Q_OS_WIN
// Windows reserves certain names for devices, which cannot be used as file names
static const QStringList reservedNames {
QStringLiteral("CON"), QStringLiteral("PRN"), QStringLiteral("AUX"), QStringLiteral("NUL"),
QStringLiteral("COM1"), QStringLiteral("COM2"), QStringLiteral("COM3"), QStringLiteral("COM4"),
QStringLiteral("COM5"), QStringLiteral("COM6"), QStringLiteral("COM7"), QStringLiteral("COM8"),
QStringLiteral("COM9"), QStringLiteral("COM¹"), QStringLiteral("COM²"), QStringLiteral("COM³"),
QStringLiteral("LPT1"), QStringLiteral("LPT2"), QStringLiteral("LPT3"), QStringLiteral("LPT4"),
QStringLiteral("LPT5"), QStringLiteral("LPT6"), QStringLiteral("LPT7"), QStringLiteral("LPT8"),
QStringLiteral("LPT9"), QStringLiteral("LPT¹"), QStringLiteral("LPT²"), QStringLiteral("LPT³")
};
const QString baseName = name.section(u'.', 0, 0);
if (reservedNames.contains(baseName, Qt::CaseInsensitive))
return true;
#endif
// Check for control characters, delete character and forward slash
for (const QChar &c : name)
{
const ushort unicode = c.unicode();
if (unicode < 32 || unicode == 127 || c == u'/')
return true;
#ifdef Q_OS_WIN
// Windows forbids reserved characters in file names
if (c == u'\\' || c == u'<' || c == u'>' || c == u':' || c == u'"' ||
c == u'|' || c == u'?' || c == u'*')
return true;
#endif
}
return false;
}
bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &value, const int role) bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &value, const int role)
{ {
if (!index.isValid()) if (!index.isValid())
@ -299,9 +349,16 @@ bool TorrentContentModel::setData(const QModelIndex &index, const QVariant &valu
case TorrentContentModelItem::COL_NAME: case TorrentContentModelItem::COL_NAME:
{ {
const QString currentName = item->name(); const QString currentName = item->name();
const QString newName = value.toString(); const QString newName = value.toString().trimmed();
if (currentName != newName) if (currentName != newName)
{ {
if (isInvalidName(newName))
{
emit renameFailed(tr("The name \"%1\" is invalid.").arg(newName));
return false;
}
try try
{ {
const Path parentPath = getItemPath(index.parent()); const Path parentPath = getItemPath(index.parent());

View file

@ -94,6 +94,7 @@ private:
void updateFilesPriorities(); void updateFilesPriorities();
void updateFilesAvailability(); void updateFilesAvailability();
bool setItemPriority(const QModelIndex &index, BitTorrent::DownloadPriority priority); bool setItemPriority(const QModelIndex &index, BitTorrent::DownloadPriority priority);
bool isInvalidName(const QString &name) const;
void notifySubtreeUpdated(const QModelIndex &index, const QList<ColumnInterval> &columns); void notifySubtreeUpdated(const QModelIndex &index, const QList<ColumnInterval> &columns);
BitTorrent::TorrentContentHandler *m_contentHandler = nullptr; BitTorrent::TorrentContentHandler *m_contentHandler = nullptr;