1 /*
2  * Copyright (C) by Kevin Ottens <kevin.ottens@nextcloud.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12  * for more details.
13  */
14 
15 #include "conflictsolver.h"
16 
17 #include <QFileDialog>
18 #include <QMessageBox>
19 
20 #include "common/utility.h"
21 #include "filesystem.h"
22 
23 namespace OCC {
24 
25 Q_LOGGING_CATEGORY(lcConflict, "nextcloud.gui.conflictsolver", QtInfoMsg)
26 
ConflictSolver(QWidget * parent)27 ConflictSolver::ConflictSolver(QWidget *parent)
28     : QObject(parent)
29     , _parentWidget(parent)
30 {
31 }
32 
localVersionFilename() const33 QString ConflictSolver::localVersionFilename() const
34 {
35     return _localVersionFilename;
36 }
37 
remoteVersionFilename() const38 QString ConflictSolver::remoteVersionFilename() const
39 {
40     return _remoteVersionFilename;
41 }
42 
exec(ConflictSolver::Solution solution)43 bool ConflictSolver::exec(ConflictSolver::Solution solution)
44 {
45     switch (solution) {
46     case KeepLocalVersion:
47         return overwriteRemoteVersion();
48     case KeepRemoteVersion:
49         return deleteLocalVersion();
50     case KeepBothVersions:
51         return renameLocalVersion();
52     }
53     Q_UNREACHABLE();
54     return false;
55 }
56 
setLocalVersionFilename(const QString & localVersionFilename)57 void ConflictSolver::setLocalVersionFilename(const QString &localVersionFilename)
58 {
59     if (_localVersionFilename == localVersionFilename) {
60         return;
61     }
62 
63     _localVersionFilename = localVersionFilename;
64     emit localVersionFilenameChanged();
65 }
66 
setRemoteVersionFilename(const QString & remoteVersionFilename)67 void ConflictSolver::setRemoteVersionFilename(const QString &remoteVersionFilename)
68 {
69     if (_remoteVersionFilename == remoteVersionFilename) {
70         return;
71     }
72 
73     _remoteVersionFilename = remoteVersionFilename;
74     emit remoteVersionFilenameChanged();
75 }
76 
deleteLocalVersion()77 bool ConflictSolver::deleteLocalVersion()
78 {
79     if (_localVersionFilename.isEmpty()) {
80         return false;
81     }
82 
83     QFileInfo info(_localVersionFilename);
84     if (!info.exists()) {
85         return false;
86     }
87 
88     const auto message = info.isDir() ? tr("Do you want to delete the directory <i>%1</i> and all its contents permanently?").arg(info.dir().dirName())
89                                       : tr("Do you want to delete the file <i>%1</i> permanently?").arg(info.fileName());
90     const auto result = QMessageBox::question(_parentWidget, tr("Confirm deletion"), message, QMessageBox::Yes, QMessageBox::No);
91     if (result != QMessageBox::Yes)
92         return false;
93 
94     if (info.isDir()) {
95         return FileSystem::removeRecursively(_localVersionFilename);
96     } else {
97         return QFile(_localVersionFilename).remove();
98     }
99 }
100 
renameLocalVersion()101 bool ConflictSolver::renameLocalVersion()
102 {
103     if (_localVersionFilename.isEmpty()) {
104         return false;
105     }
106 
107     QFileInfo info(_localVersionFilename);
108     if (!info.exists()) {
109         return false;
110     }
111 
112     const auto renamePattern = [=] {
113         auto result = QString::fromUtf8(OCC::Utility::conflictFileBaseNameFromPattern(_localVersionFilename.toUtf8()));
114         const auto dotIndex = result.lastIndexOf('.');
115         return QString(result.left(dotIndex) + "_%1" + result.mid(dotIndex));
116     }();
117 
118     const auto targetFilename = [=] {
119         uint i = 1;
120         auto result = renamePattern.arg(i);
121         while (QFileInfo::exists(result)) {
122             Q_ASSERT(i > 0);
123             i++;
124             result = renamePattern.arg(i);
125         }
126         return result;
127     }();
128 
129     QString error;
130     if (FileSystem::uncheckedRenameReplace(_localVersionFilename, targetFilename, &error)) {
131         return true;
132     } else {
133         qCWarning(lcConflict) << "Rename error:" << error;
134         QMessageBox::warning(_parentWidget, tr("Error"), tr("Moving file failed:\n\n%1").arg(error));
135         return false;
136     }
137 }
138 
overwriteRemoteVersion()139 bool ConflictSolver::overwriteRemoteVersion()
140 {
141     if (_localVersionFilename.isEmpty()) {
142         return false;
143     }
144 
145     if (_remoteVersionFilename.isEmpty()) {
146         return false;
147     }
148 
149     QFileInfo info(_localVersionFilename);
150     if (!info.exists()) {
151         return false;
152     }
153 
154     QString error;
155     if (FileSystem::uncheckedRenameReplace(_localVersionFilename, _remoteVersionFilename, &error)) {
156         return true;
157     } else {
158         qCWarning(lcConflict) << "Rename error:" << error;
159         QMessageBox::warning(_parentWidget, tr("Error"), tr("Moving file failed:\n\n%1").arg(error));
160         return false;
161     }
162 }
163 
164 } // namespace OCC
165