Fix dbus assert when hostname contains non-Latin characters

This commit is contained in:
Mat Sutcliffe
2022-02-11 20:13:41 +00:00
parent 20f8609357
commit 4703edb841
5 changed files with 79 additions and 7 deletions

View File

@@ -21,27 +21,25 @@ BLACK_DEFINE_VALUEOBJECT_MIXINS(BlackMisc, CIdentifier)
//! \private Escape characters not allowed in dbus paths
QString toDBusPath(const QString &s)
{
Q_ASSERT_X(!BlackMisc::containsChar(s, [](QChar c) { return c.unicode() > 0x7f; }), Q_FUNC_INFO, "7-bit ASCII only");
return s.toLatin1().toPercentEncoding("/", "-._~", '_');
return BlackMisc::utfToPercentEncoding(s, "/", '_');
}
//! \private Escape characters not allowed in dbus path elements
QString toDBusPathElement(const QString &s)
{
Q_ASSERT_X(!BlackMisc::containsChar(s, [](QChar c) { return c.unicode() > 0x7f; }), Q_FUNC_INFO, "7-bit ASCII only");
return s.toLatin1().toPercentEncoding({}, "-._~", '_');
return BlackMisc::utfToPercentEncoding(s, {}, '_');
}
//! \private Unescape characters not allowed in dbus paths
QString fromDBusPath(const QString &s)
{
return QByteArray::fromPercentEncoding(s.toLatin1(), '_');
return BlackMisc::utfFromPercentEncoding(s.toLatin1(), '_');
}
//! \private Unescape characters not allowed in dbus path elements
QString fromDBusPathElement(const QString &s)
{
return QByteArray::fromPercentEncoding(s.toLatin1(), '_');
return BlackMisc::utfFromPercentEncoding(s.toLatin1(), '_');
}
//! \private

View File

@@ -34,6 +34,69 @@ namespace BlackMisc
return splitString(s, [](QChar c) { return c == '\n' || c == '\r'; });
}
QByteArray utfToPercentEncoding(const QString& s, const QByteArray &allow, char percent)
{
QByteArray result;
for (const QChar &c : s)
{
if (const char latin = c.toLatin1())
{
if ((latin >= 'a' && latin <= 'z') || (latin >= 'A' && latin <= 'Z')
|| (latin >= '0' && latin <= '9') || allow.contains(latin))
{
result += c;
}
else
{
result += percent;
if (latin < 0x10) { result += '0'; }
result += QByteArray::number(static_cast<int>(latin), 16);
}
}
else
{
result += percent;
result += 'x';
const ushort unicode = c.unicode();
if (unicode < 0x0010) { result += '0'; }
if (unicode < 0x0100) { result += '0'; }
if (unicode < 0x1000) { result += '0'; }
result += QByteArray::number(unicode, 16);
}
}
return result;
}
QString utfFromPercentEncoding(const QByteArray& ba, char percent)
{
QString result;
for (int i = 0; i < ba.size(); ++i)
{
if (ba[i] == percent)
{
++i;
Q_ASSERT(i < ba.size());
if (ba[i] == 'x')
{
++i;
Q_ASSERT(i < ba.size());
result += QChar(ba.mid(i, 4).toInt(nullptr, 16));
i += 3;
}
else
{
result += static_cast<char>(ba.mid(i, 2).toInt(nullptr, 16));
++i;
}
}
else
{
result += ba[i];
}
}
return result;
}
const QString &boolToOnOff(bool v)
{
static const QString on("on");

View File

@@ -126,6 +126,12 @@ namespace BlackMisc
//! Split a string into multiple lines. Blank lines are skipped.
BLACKMISC_EXPORT QStringList splitLines(const QString &s);
//! Extended percent encoding supporting UTF-16
BLACKMISC_EXPORT QByteArray utfToPercentEncoding(const QString &s, const QByteArray &allow = {}, char percent = '%');
//! Reverse utfFromPercentEncoding
BLACKMISC_EXPORT QString utfFromPercentEncoding(const QByteArray &ba, char percent = '%');
//! A map converted to string
template<class K, class V> QString qmapToString(const QMap<K, V> &map)
{

View File

@@ -73,7 +73,7 @@ namespace BlackMiscTest
void CTestIdentifier::dbusObjectPath()
{
QObject q;
q.setObjectName("!@#$%^&*()_+");
q.setObjectName(QString::fromUtf16(u"!@#$%^&*()_+\u263a"));
CTestIdentifiable id(&q);
QString s(id.identifier().toDBusObjectPath());
QVERIFY2(id.identifier() == CIdentifier::fromDBusObjectPath(s), "Conversion from dbus object path and back compares equal");

View File

@@ -129,12 +129,17 @@ namespace BlackMiscTest
//! ctor
Server()
{
QObject::connect(&m_process, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), [](int code, QProcess::ExitStatus status)
{
qDebug() << "Server process exited" << (status ? "abnormally" : "normally") << "with exit code" << code;
});
m_process.start("sharedstatetestserver", QStringList());
m_process.waitForStarted();
}
//! dtor
~Server()
{
m_process.disconnect();
m_process.kill();
m_process.waitForFinished();
}