1 /*
2  * KDiff3 - Text Diff And Merge Tool
3  *
4  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
5  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
6  * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #ifndef FILEACCESS_H
10 #define FILEACCESS_H
11 
12 #include <type_traits>
13 
14 #include <QDateTime>
15 #include <QDir>
16 #include <QFile>
17 #include <QFileInfo>
18 #include <QSharedPointer>
19 #include <QTemporaryFile>
20 #include <QUrl>
21 
22 #ifndef AUTOTEST
23 #include <KIO/UDSEntry>
24 #endif
25 
26 class t_DirectoryList;
27 class FileAccessJobHandler;
28 class DefaultFileAccessJobHandler;
29 /*
30   Defining a function as virtual in FileAccess is intended to allow testing sub classes to be written
31   more easily. This way the test can use a moc class that emulates the needed conditions with no
32   actual file being present. This would otherwise be a technical and logistical nightmare.
33 */
34 class FileAccess
35 {
36   public:
37     FileAccess();
38 
39     FileAccess(const FileAccess&);
40     FileAccess(FileAccess&&) noexcept;
41     FileAccess& operator=(const FileAccess&);
42     FileAccess& operator=(FileAccess&&) noexcept;
43     virtual ~FileAccess();
44     explicit FileAccess(const QString& name, bool bWantToWrite = false); // name: local file or dirname or url (when supported)
45 
46     explicit FileAccess(const QUrl& name, bool bWantToWrite = false); // name: local file or dirname or url (when supported)
47     virtual void setFile(const QString& name, bool bWantToWrite = false);
48     virtual void setFile(const QUrl& url, bool bWantToWrite = false);
49     virtual void setFile(FileAccess* pParent, const QFileInfo& fi);
50 
51     virtual void loadData();
52 
53     Q_REQUIRED_RESULT bool isNormal() const;
54     Q_REQUIRED_RESULT bool isValid() const;
55     Q_REQUIRED_RESULT virtual bool isFile() const;
56     Q_REQUIRED_RESULT virtual bool isDir() const;
57     Q_REQUIRED_RESULT virtual bool isSymLink() const;
58     Q_REQUIRED_RESULT virtual bool exists() const;
59     Q_REQUIRED_RESULT virtual qint64 size() const;     // Size as returned by stat().
60     Q_REQUIRED_RESULT virtual qint64 sizeForReading(); // If the size can't be determined by stat() then the file is copied to a local temp file.
61     Q_REQUIRED_RESULT virtual bool isReadable() const;
62     Q_REQUIRED_RESULT virtual bool isWritable() const;
63     Q_REQUIRED_RESULT virtual bool isExecutable() const;
64     Q_REQUIRED_RESULT virtual bool isHidden() const;
65     Q_REQUIRED_RESULT QString readLink() const;
66 
67     Q_REQUIRED_RESULT QDateTime lastModified() const;
68 
displayName()69     Q_REQUIRED_RESULT QString displayName() const { return mDisplayName.isEmpty() ? fileName() : mDisplayName; }
70     Q_REQUIRED_RESULT QString fileName(bool needTmp = false) const; // Just the name-part of the path, without parent directories
71     Q_REQUIRED_RESULT QString fileRelPath() const;                  // The path relative to base comparison directory
72     Q_REQUIRED_RESULT QString prettyAbsPath() const;
73     Q_REQUIRED_RESULT QUrl url() const;
setUrl(const QUrl & inUrl)74     void setUrl(const QUrl& inUrl) { m_url = inUrl; }
75 
76     //Workaround for QUrl::toDisplayString/QUrl::toString behavior that does not fit KDiff3's expectations
77     Q_REQUIRED_RESULT QString absoluteFilePath() const;
prettyAbsPath(const QUrl & url)78     Q_REQUIRED_RESULT static QString prettyAbsPath(const QUrl& url)
79     {
80         if(!isLocal(url)) return url.toDisplayString();
81 
82         return QFileInfo(url.path()).absoluteFilePath();
83     }
84 
85     //Workaround for QUrl::isLocalFile behavior that does not fit KDiff3's expectations.
86     Q_REQUIRED_RESULT bool isLocal() const;
isLocal(const QUrl & url)87     Q_REQUIRED_RESULT static bool isLocal(const QUrl& url)
88     {
89         return url.isLocalFile() || !url.isValid() || url.scheme().isEmpty();
90     }
91 
92     virtual bool readFile(void* pDestBuffer, qint64 maxLength);
93     virtual bool writeFile(const void* pSrcBuffer, qint64 length);
94     bool listDir(t_DirectoryList* pDirList, bool bRecursive, bool bFindHidden,
95                  const QString& filePattern, const QString& fileAntiPattern,
96                  const QString& dirAntiPattern, bool bFollowDirLinks, bool bUseCvsIgnore);
97     virtual bool copyFile(const QString& destUrl);
98     virtual bool createBackup(const QString& bakExtension);
99 
100     Q_REQUIRED_RESULT QString getTempName() const;
101     virtual bool createLocalCopy();
102     static void createTempFile(QTemporaryFile&);
103     bool removeFile();
104     static bool makeDir(const QString&);
105     static bool removeDir(const QString&);
106     static bool exists(const QString&);
107     static QString cleanPath(const QString&);
108 
109     //bool chmod( const QString& );
110     bool rename(const FileAccess&);
111     static bool symLink(const QString& linkTarget, const QString& linkLocation);
112 
113     virtual void addPath(const QString& txt, bool reinit = true);
114     Q_REQUIRED_RESULT const QString& getStatusText() const;
115 
116     Q_REQUIRED_RESULT FileAccess* parent() const; // !=0 for listDir-results, but only valid if the parent was not yet destroyed.
117 
118     void doError();
119     void filterList(t_DirectoryList* pDirList, const QString& filePattern,
120                     const QString& fileAntiPattern, const QString& dirAntiPattern,
121                     const bool bUseCvsIgnore);
122 
getBaseDirectory()123     Q_REQUIRED_RESULT QDir getBaseDirectory() const { return m_baseDir; }
124 
125     bool open(const QFile::OpenMode flags);
126 
127     qint64 read(char* data, const qint64 maxlen);
128     void close();
129 
130     const QString& errorString() const;
131 
132     //These should be exposed for auto tests
133   protected:
134 #ifndef AUTOTEST
135     friend DefaultFileAccessJobHandler;
136     void setFromUdsEntry(const KIO::UDSEntry& e, FileAccess* parent);
137 #endif
138     void setStatusText(const QString& s);
139 
140     void reset();
141 
142     bool interruptableReadFile(void* pDestBuffer, qint64 maxLength);
143 
144     QScopedPointer<FileAccessJobHandler> mJobHandler;
145     FileAccess* m_pParent = nullptr;
146     QUrl m_url;
147     bool m_bValidData = false;
148 
149     QDir m_baseDir;
150     QFileInfo m_fileInfo;
151     QString m_linkTarget;
152     QString m_name;
153 
154     QString mDisplayName;
155     QString m_localCopy;
156     QString mPhysicalPath;
157     QSharedPointer<QTemporaryFile> tmpFile = QSharedPointer<QTemporaryFile>::create();
158     QSharedPointer<QFile> realFile = nullptr;
159 
160     qint64 m_size = 0;
161     QDateTime m_modificationTime = QDateTime::fromMSecsSinceEpoch(0);
162     bool m_bSymLink = false;
163     bool m_bFile = false;
164     bool m_bDir = false;
165     bool m_bExists = false;
166     bool m_bWritable = false;
167     bool m_bReadable = false;
168     bool m_bExecutable = false;
169     bool m_bHidden = false;
170 
171     QString m_statusText; // Might contain an error string, when the last operation didn't succeed.
172 
173   private:
174     /*
175     These two variables are used to prevent infinate/long running loops when a symlinks true target
176     must be found. isNormal is right now the only place this is needed.
177 
178     Never expose these outside FileAccess as they are internal values.
179     */
180     mutable bool mVisited = false;
181     mutable quint32 mDepth = 0;
182 };
183 /*
184  FileAccess objects should be copy and move assignable.
185   Used a few places in KDiff3 itself.
186   Also used in std::list<FileAccess>
187 */
188 static_assert(std::is_copy_assignable<FileAccess>::value, "FileAccess must be copy assignable.");
189 static_assert(std::is_move_assignable<FileAccess>::value, "FileAccess must be move assignable.");
190 
191 class t_DirectoryList: public std::list<FileAccess>
192 {
193 };
194 
195 #endif
196