diff --git a/src/csync/csync_update.cpp b/src/csync/csync_update.cpp index 74a53c84b..458c8d918 100644 --- a/src/csync/csync_update.cpp +++ b/src/csync/csync_update.cpp @@ -150,10 +150,18 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr f } } - if (ctx->current == REMOTE_REPLICA && QTextCodec::codecForLocale()->mibEnum() != 106) { + auto localCodec = QTextCodec::codecForLocale(); + if (ctx->current == REMOTE_REPLICA && localCodec->mibEnum() != 106) { /* If the locale codec is not UTF-8, we must check that the filename from the server can - * be encoded in the local file system. */ - if (!QTextCodec::codecForLocale()->canEncode(QString::fromUtf8(fs->path))) { + * be encoded in the local file system. + * + * We cannot use QTextCodec::canEncode() since that can incorrectly return true, see + * https://bugreports.qt.io/browse/QTBUG-6925. + */ + QTextEncoder encoder(localCodec, QTextCodec::ConvertInvalidToNull); + if (encoder.fromUnicode(QString::fromUtf8(fs->path)).contains('\0')) { + qCDebug(lcUpdate, "cannot encode %s to local encoding %d", + fs->path.constData(), localCodec->mibEnum()); excluded = CSYNC_FILE_EXCLUDE_CANNOT_ENCODE; } } diff --git a/test/testsyncengine.cpp b/test/testsyncengine.cpp index 10e5e9acd..22509388f 100644 --- a/test/testsyncengine.cpp +++ b/test/testsyncengine.cpp @@ -586,6 +586,56 @@ private slots: QVERIFY(localFileExists("A/.hidden")); QVERIFY(fakeFolder.currentRemoteState().find("B/.hidden")); } + + void testNoLocalEncoding() + { + auto utf8Locale = QTextCodec::codecForLocale(); + if (utf8Locale->mibEnum() != 106) { + QSKIP("Test only works for UTF8 locale"); + } + + FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() }; + QVERIFY(fakeFolder.syncOnce()); + QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState()); + + // Utf8 locale can sync both + fakeFolder.remoteModifier().insert("A/tößt"); + fakeFolder.remoteModifier().insert("A/t𠜎t"); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(fakeFolder.currentLocalState().find("A/tößt")); + QVERIFY(fakeFolder.currentLocalState().find("A/t𠜎t")); + + // Try again with a locale that can represent ö but not 𠜎 (4-byte utf8). + QTextCodec::setCodecForLocale(QTextCodec::codecForName("ISO-8859-15")); + QVERIFY(QTextCodec::codecForLocale()->mibEnum() == 111); + + fakeFolder.remoteModifier().insert("B/tößt"); + fakeFolder.remoteModifier().insert("B/t𠜎t"); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(fakeFolder.currentLocalState().find("B/tößt")); + QVERIFY(!fakeFolder.currentLocalState().find("B/t𠜎t")); + QVERIFY(!fakeFolder.currentLocalState().find("B/t?t")); + QVERIFY(!fakeFolder.currentLocalState().find("B/t??t")); + QVERIFY(!fakeFolder.currentLocalState().find("B/t???t")); + QVERIFY(!fakeFolder.currentLocalState().find("B/t????t")); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(fakeFolder.currentRemoteState().find("B/tößt")); + QVERIFY(fakeFolder.currentRemoteState().find("B/t𠜎t")); + + // Try again with plain ascii + QTextCodec::setCodecForLocale(QTextCodec::codecForName("ASCII")); + QVERIFY(QTextCodec::codecForLocale()->mibEnum() == 3); + + fakeFolder.remoteModifier().insert("C/tößt"); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(!fakeFolder.currentLocalState().find("C/tößt")); + QVERIFY(!fakeFolder.currentLocalState().find("C/t??t")); + QVERIFY(!fakeFolder.currentLocalState().find("C/t????t")); + QVERIFY(fakeFolder.syncOnce()); + QVERIFY(fakeFolder.currentRemoteState().find("C/tößt")); + + QTextCodec::setCodecForLocale(utf8Locale); + } }; QTEST_GUILESS_MAIN(TestSyncEngine)