mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-22 13:05:51 +03:00
Merge pull request #5801 from nextcloud/bugfix/unsupported-filename-on-server
Bugfix/unsupported filename on server
This commit is contained in:
commit
d57eba8890
11 changed files with 99 additions and 14 deletions
|
@ -64,12 +64,13 @@ QString illegalCharacterListToString(const QVector<QChar> &illegalCharacters)
|
|||
|
||||
namespace OCC {
|
||||
|
||||
InvalidFilenameDialog::InvalidFilenameDialog(AccountPtr account, Folder *folder, QString filePath, QWidget *parent)
|
||||
InvalidFilenameDialog::InvalidFilenameDialog(AccountPtr account, Folder *folder, QString filePath, FileLocation fileLocation, QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, _ui(new Ui::InvalidFilenameDialog)
|
||||
, _account(account)
|
||||
, _folder(folder)
|
||||
, _filePath(std::move(filePath))
|
||||
, _fileLocation(fileLocation)
|
||||
{
|
||||
Q_ASSERT(_account);
|
||||
Q_ASSERT(_folder);
|
||||
|
@ -103,7 +104,12 @@ InvalidFilenameDialog::InvalidFilenameDialog(AccountPtr account, Folder *folder,
|
|||
connect(_ui->filenameLineEdit, &QLineEdit::textChanged, this,
|
||||
&InvalidFilenameDialog::onFilenameLineEditTextChanged);
|
||||
|
||||
checkIfAllowedToRename();
|
||||
if (_fileLocation == FileLocation::NewLocalFile) {
|
||||
allowRenaming();
|
||||
_ui->errorLabel->setText({});
|
||||
} else {
|
||||
checkIfAllowedToRename();
|
||||
}
|
||||
}
|
||||
|
||||
InvalidFilenameDialog::~InvalidFilenameDialog() = default;
|
||||
|
@ -136,13 +142,7 @@ void InvalidFilenameDialog::onCheckIfAllowedToRenameComplete(const QVariantMap &
|
|||
}
|
||||
}
|
||||
|
||||
_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
_ui->filenameLineEdit->setEnabled(true);
|
||||
_ui->filenameLineEdit->selectAll();
|
||||
|
||||
const auto filePathFileInfo = QFileInfo(_filePath);
|
||||
const auto fileName = filePathFileInfo.fileName();
|
||||
processLeadingOrTrailingSpacesError(fileName);
|
||||
allowRenaming();
|
||||
}
|
||||
|
||||
bool InvalidFilenameDialog::processLeadingOrTrailingSpacesError(const QString &fileName)
|
||||
|
@ -184,6 +184,17 @@ void InvalidFilenameDialog::onPropfindPermissionError(QNetworkReply *reply)
|
|||
onCheckIfAllowedToRenameComplete({}, reply);
|
||||
}
|
||||
|
||||
void InvalidFilenameDialog::allowRenaming()
|
||||
{
|
||||
_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
_ui->filenameLineEdit->setEnabled(true);
|
||||
_ui->filenameLineEdit->selectAll();
|
||||
|
||||
const auto filePathFileInfo = QFileInfo(_filePath);
|
||||
const auto fileName = filePathFileInfo.fileName();
|
||||
processLeadingOrTrailingSpacesError(fileName);
|
||||
}
|
||||
|
||||
void InvalidFilenameDialog::useInvalidName()
|
||||
{
|
||||
emit acceptedInvalidName(_filePath);
|
||||
|
|
|
@ -35,7 +35,12 @@ class InvalidFilenameDialog : public QDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InvalidFilenameDialog(AccountPtr account, Folder *folder, QString filePath, QWidget *parent = nullptr);
|
||||
enum class FileLocation {
|
||||
Default = 0,
|
||||
NewLocalFile,
|
||||
};
|
||||
|
||||
explicit InvalidFilenameDialog(AccountPtr account, Folder *folder, QString filePath, FileLocation fileLocation = FileLocation::Default, QWidget *parent = nullptr);
|
||||
|
||||
~InvalidFilenameDialog() override;
|
||||
|
||||
|
@ -53,6 +58,7 @@ private:
|
|||
QString _relativeFilePath;
|
||||
QString _originalFileName;
|
||||
QString _newFilename;
|
||||
FileLocation _fileLocation = FileLocation::Default;
|
||||
|
||||
void onFilenameLineEditTextChanged(const QString &text);
|
||||
void onMoveJobFinished();
|
||||
|
@ -65,6 +71,7 @@ private:
|
|||
bool processLeadingOrTrailingSpacesError(const QString &fileName);
|
||||
void onPropfindPermissionSuccess(const QVariantMap &values);
|
||||
void onPropfindPermissionError(QNetworkReply *reply = nullptr);
|
||||
void allowRenaming();
|
||||
private slots:
|
||||
void useInvalidName();
|
||||
};
|
||||
|
|
|
@ -230,6 +230,7 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||
|| a._syncFileItemStatus == SyncFileItem::Restoration
|
||||
|| a._syncFileItemStatus == SyncFileItem::FileLocked
|
||||
|| a._syncFileItemStatus == SyncFileItem::FileNameInvalid
|
||||
|| a._syncFileItemStatus == SyncFileItem::FileNameInvalidOnServer
|
||||
|| a._syncFileItemStatus == SyncFileItem::FileNameClash) {
|
||||
colorIconPath.append("state-warning.svg");
|
||||
return colorIconPath;
|
||||
|
@ -348,7 +349,8 @@ QVariant ActivityListModel::data(const QModelIndex &index, int role) const
|
|||
return !a._links.isEmpty() &&
|
||||
a._syncFileItemStatus != SyncFileItem::FileNameClash &&
|
||||
a._syncFileItemStatus != SyncFileItem::Conflict &&
|
||||
a._syncFileItemStatus != SyncFileItem::FileNameInvalid;
|
||||
a._syncFileItemStatus != SyncFileItem::FileNameInvalid &&
|
||||
a._syncFileItemStatus != SyncFileItem::FileNameInvalidOnServer;
|
||||
case IsCurrentUserFileActivityRole:
|
||||
return a._isCurrentUserFileActivity;
|
||||
case ThumbnailRole: {
|
||||
|
@ -656,15 +658,20 @@ void ActivityListModel::slotTriggerDefaultAction(const int activityIndex)
|
|||
} else if (activity._syncFileItemStatus == SyncFileItem::FileNameClash) {
|
||||
triggerCaseClashAction(activity);
|
||||
return;
|
||||
} else if (activity._syncFileItemStatus == SyncFileItem::FileNameInvalid) {
|
||||
} else if (activity._syncFileItemStatus == SyncFileItem::FileNameInvalid
|
||||
|| activity._syncFileItemStatus == SyncFileItem::FileNameInvalidOnServer) {
|
||||
if (!_currentInvalidFilenameDialog.isNull()) {
|
||||
_currentInvalidFilenameDialog->close();
|
||||
}
|
||||
|
||||
auto folder = FolderMan::instance()->folder(activity._folder);
|
||||
const auto folderDir = QDir(folder->path());
|
||||
const auto fileLocation = activity._syncFileItemStatus == SyncFileItem::FileNameInvalidOnServer
|
||||
? InvalidFilenameDialog::FileLocation::NewLocalFile
|
||||
: InvalidFilenameDialog::FileLocation::Default;
|
||||
|
||||
_currentInvalidFilenameDialog = new InvalidFilenameDialog(_accountState->account(), folder,
|
||||
folderDir.filePath(activity._file));
|
||||
folderDir.filePath(activity._file), fileLocation);
|
||||
connect(_currentInvalidFilenameDialog, &InvalidFilenameDialog::accepted, folder, [folder]() {
|
||||
folder->scheduleThisFolderSoon();
|
||||
});
|
||||
|
|
|
@ -342,6 +342,11 @@ QString AbstractNetworkJob::errorStringParsingBody(QByteArray *body)
|
|||
return base;
|
||||
}
|
||||
|
||||
QString AbstractNetworkJob::errorStringParsingBodyException(const QByteArray &body) const
|
||||
{
|
||||
return extractException(body);
|
||||
}
|
||||
|
||||
AbstractNetworkJob::~AbstractNetworkJob()
|
||||
{
|
||||
setReply(nullptr);
|
||||
|
@ -423,6 +428,23 @@ QString extractErrorMessage(const QByteArray &errorResponse)
|
|||
return exception;
|
||||
}
|
||||
|
||||
QString extractException(const QByteArray &errorResponse)
|
||||
{
|
||||
QXmlStreamReader reader(errorResponse);
|
||||
reader.readNextStartElement();
|
||||
if (reader.name() != QLatin1String("error")) {
|
||||
return {};
|
||||
}
|
||||
|
||||
while (!reader.atEnd() && !reader.hasError()) {
|
||||
reader.readNextStartElement();
|
||||
if (reader.name() == QLatin1String("exception")) {
|
||||
return reader.readElementText();
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QString errorMessage(const QString &baseError, const QByteArray &body)
|
||||
{
|
||||
QString msg = baseError;
|
||||
|
|
|
@ -91,6 +91,18 @@ public:
|
|||
*/
|
||||
QString errorStringParsingBody(QByteArray *body = nullptr);
|
||||
|
||||
/** Like errorString, but also checking the reply body for information.
|
||||
*
|
||||
* Specifically, sometimes xml bodies have extra error information.
|
||||
* This function reads the body of the reply and parses out the
|
||||
* error information, if possible.
|
||||
*
|
||||
* \a body is optinally filled with the reply body.
|
||||
*
|
||||
* Warning: Needs to call reply()->readAll().
|
||||
*/
|
||||
[[nodiscard]] QString errorStringParsingBodyException(const QByteArray &body) const;
|
||||
|
||||
/** Make a new request */
|
||||
void retry();
|
||||
|
||||
|
@ -233,6 +245,16 @@ private:
|
|||
*/
|
||||
QString OWNCLOUDSYNC_EXPORT extractErrorMessage(const QByteArray &errorResponse);
|
||||
|
||||
|
||||
/** Gets the SabreDAV-style exception from an error response.
|
||||
*
|
||||
* This assumes the response is XML with a 'exception' tag that has a
|
||||
* 'exception' tag that contains the data to extract.
|
||||
*
|
||||
* Returns a null string if no message was found.
|
||||
*/
|
||||
[[nodiscard]] QString OWNCLOUDSYNC_EXPORT extractException(const QByteArray &errorResponse);
|
||||
|
||||
/** Builds a error message based on the error and the reply body. */
|
||||
QString OWNCLOUDSYNC_EXPORT errorMessage(const QString &baseError, const QByteArray &body);
|
||||
|
||||
|
|
|
@ -733,6 +733,7 @@ void BulkPropagatorJob::handleJobDoneErrors(SyncFileItemPtr item,
|
|||
case SyncFileItem::FileIgnored:
|
||||
case SyncFileItem::FileLocked:
|
||||
case SyncFileItem::FileNameInvalid:
|
||||
case SyncFileItem::FileNameInvalidOnServer:
|
||||
case SyncFileItem::FileNameClash:
|
||||
case SyncFileItem::NoStatus:
|
||||
case SyncFileItem::NormalError:
|
||||
|
|
|
@ -274,6 +274,7 @@ void PropagateItemJob::done(const SyncFileItem::Status statusArg, const QString
|
|||
case SyncFileItem::BlacklistedError:
|
||||
case SyncFileItem::FileLocked:
|
||||
case SyncFileItem::FileNameInvalid:
|
||||
case SyncFileItem::FileNameInvalidOnServer:
|
||||
case SyncFileItem::FileNameClash:
|
||||
// nothing
|
||||
break;
|
||||
|
@ -1507,6 +1508,7 @@ void PropagateRootDirectory::slotSubJobsFinished(SyncFileItem::Status status)
|
|||
case SyncFileItem::FileLocked:
|
||||
case SyncFileItem::Restoration:
|
||||
case SyncFileItem::FileNameInvalid:
|
||||
case SyncFileItem::FileNameInvalidOnServer:
|
||||
case SyncFileItem::DetailError:
|
||||
case SyncFileItem::Success:
|
||||
break;
|
||||
|
|
|
@ -101,6 +101,7 @@ bool Progress::isWarningKind(SyncFileItem::Status kind)
|
|||
|| kind == SyncFileItem::Conflict || kind == SyncFileItem::Restoration
|
||||
|| kind == SyncFileItem::DetailError || kind == SyncFileItem::BlacklistedError
|
||||
|| kind == SyncFileItem::FileLocked || kind == SyncFileItem::FileNameInvalid
|
||||
|| kind == SyncFileItem::FileNameInvalidOnServer
|
||||
|| kind == SyncFileItem::FileNameClash;
|
||||
}
|
||||
|
||||
|
|
|
@ -694,6 +694,13 @@ void PropagateUploadFileCommon::commonErrorHandling(AbstractNetworkJob *job)
|
|||
status = SyncFileItem::DetailError;
|
||||
errorString = tr("Upload of %1 exceeds the quota for the folder").arg(Utility::octetsToString(_fileToUpload._size));
|
||||
emit propagator()->insufficientRemoteStorage();
|
||||
} else if (_item->_httpErrorCode == 400) {
|
||||
const auto exception = job->errorStringParsingBodyException(replyContent);
|
||||
|
||||
if (exception.endsWith(QStringLiteral("\\InvalidPath"))) {
|
||||
errorString = tr("Unable to upload an item with invalid characters");
|
||||
status = SyncFileItem::FileNameInvalidOnServer;
|
||||
}
|
||||
}
|
||||
|
||||
abortWithError(status, errorString);
|
||||
|
|
|
@ -72,6 +72,11 @@ public:
|
|||
*/
|
||||
FileNameInvalid,
|
||||
|
||||
/**
|
||||
* The filename contains invalid characters and can not be uploaded to the server
|
||||
*/
|
||||
FileNameInvalidOnServer,
|
||||
|
||||
/**
|
||||
* There is a file name clash (e.g. attempting to download test.txt when TEST.TXT already exists
|
||||
* on a platform where the filesystem is case-insensitive
|
||||
|
|
|
@ -141,7 +141,7 @@ void SyncResult::processCompletedItem(const SyncFileItemPtr &item)
|
|||
if (!_firstItemError) {
|
||||
_firstItemError = item;
|
||||
}
|
||||
} else if (item->_status == SyncFileItem::Conflict || item->_status == SyncFileItem::FileNameInvalid || item->_status == SyncFileItem::FileNameClash) {
|
||||
} else if (item->_status == SyncFileItem::Conflict || item->_status == SyncFileItem::FileNameInvalid || item->_status == SyncFileItem::FileNameInvalidOnServer || item->_status == SyncFileItem::FileNameClash) {
|
||||
if (item->_instruction == CSYNC_INSTRUCTION_CONFLICT) {
|
||||
_numNewConflictItems++;
|
||||
if (!_firstNewConflictItem) {
|
||||
|
|
Loading…
Reference in a new issue