1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 
8 #include "util_file.h"
9 
10 #ifdef _MSC_VER
11 # include <sys/utime.h>
12 #else
13 # include <utime.h>
14 #endif
15 
16 #include <QByteArray>
17 #include <QDataStream>
18 #include <QDir>
19 #include <QFile>
20 #include <QFileInfo>
21 #include <QString>
22 #include <QProcess>
23 #include <QScopedPointer>
24 #include <QTemporaryFile>
25 
26 #include "fileloader.h"
27 #include "loadsaveplugin.h"
28 #include "prefsmanager.h"
29 #include "scpaths.h"
30 #include "scribusdoc.h"
31 #include "scstreamfilter.h"
32 #include "selection.h"
33 #include "util.h"
34 
copyData(QIODevice & src,QIODevice & dest)35 bool copyData(QIODevice& src, QIODevice& dest)
36 {
37 	bool success = false;
38 	if ((src.openMode() & QIODevice::ReadOnly) == 0)
39 		return false;
40 	if ((dest.openMode() & QIODevice::WriteOnly) == 0)
41 		return false;
42 	QByteArray bb( 65536, ' ' );
43 	if (bb.size() > 0) // Check for memory allocation failure
44 	{
45 		qint64 byteswritten;
46 		qint64 bytesread = src.read( bb.data(), bb.size() );
47 		success = (bytesread > 0);
48 		while (bytesread > 0)
49 		{
50 			byteswritten = dest.write( bb.data(), bytesread );
51 			success  &= (bytesread == byteswritten);
52 			bytesread = src.read( bb.data(), bb.size() );
53 		}
54 	}
55 	return success;
56 }
57 
copyFile(const QString & source,const QString & target)58 bool copyFile(const QString& source, const QString& target)
59 {
60 	bool success = true;
61 	if ((source.isEmpty()) || (target.isEmpty()))
62 		return false;
63 	if (source == target)
64 		return false;
65 	QFile s(source);
66 	if (!s.exists())
67 		return false;
68 	QFile t(target);
69 	if (s.open(QIODevice::ReadOnly))
70 	{
71 		if (t.open(QIODevice::WriteOnly))
72 		{
73 			success  = copyData(s, t);
74 			success &= (s.error() == QFile::NoError && t.error() == QFile::NoError);
75 			t.close();
76 		}
77 		s.close();
78 	}
79 	return success;
80 }
81 
copyFileAtomic(const QString & source,const QString & target)82 bool copyFileAtomic(const QString& source, const QString& target)
83 {
84 	bool success = false;
85 	if ((source.isEmpty()) || (target.isEmpty()))
86 		return false;
87 	if (source == target)
88 		return false;
89 	QFile srcFile(source);
90 	QString tempFileName;
91 	QTemporaryFile* tempFile = new QTemporaryFile(target + "_XXXXXX");
92 	if (!tempFile)
93 		return false;
94 	if (srcFile.open(QIODevice::ReadOnly))
95 	{
96 		if (tempFile->open())
97 		{
98 			tempFileName = tempFile->fileName();
99 			success  = copyData(srcFile, *tempFile);
100 			success &= (srcFile.error() == QFile::NoError && tempFile->error() == QFile::NoError);
101 			tempFile->close();
102 		}
103 		srcFile.close();
104 	}
105 	if (success)
106 	{
107 		if (QFile::exists(target))
108 			success = QFile::remove(target);
109 		if (success)
110 		{
111 			// We delete temporary file now to force file close
112 			// QTemporaryFile::close() do not really close file
113 			tempFile->setAutoRemove(false);
114 			delete tempFile;
115 			tempFile = nullptr;
116 			success = QFile::rename(tempFileName, target);
117 		}
118 	}
119 	delete tempFile;
120 	return success;
121 }
122 
copyFileToFilter(const QString & source,ScStreamFilter & target)123 bool copyFileToFilter(const QString& source, ScStreamFilter& target)
124 {
125 	bool copySucceed = true;
126 	if (source.isEmpty())
127 		return false;
128 	if (!QFile::exists(source))
129 		return false;
130 	QFile s(source);
131 	QByteArray bb( 65536, ' ' );
132 	if (bb.size() <= 0) // Check for memory allocation failure
133 		return false;
134 	if (s.open(QIODevice::ReadOnly))
135 	{
136 		int bytesread = s.read( bb.data(), bb.size() );
137 		while (bytesread > 0)
138 		{
139 			copySucceed &= target.writeData(bb.data(), bytesread);
140 			bytesread = s.read( bb.data(), bb.size() );
141 		}
142 		copySucceed &= (s.error() == QFile::NoError);
143 		s.close();
144 	}
145 	return copySucceed;
146 }
147 
copyFileToStream(const QString & source,QDataStream & target)148 bool copyFileToStream(const QString& source, QDataStream& target)
149 {
150 	bool copySucceed = true;
151 	if (source.isEmpty())
152 		return false;
153 	if (!QFile::exists(source))
154 		return false;
155 	if (!target.device()->isOpen() || !target.device()->isWritable())
156 		return false;
157 	QFile s(source);
158 	QByteArray bb( 65536, ' ' );
159 	if (bb.size() <= 0) // Check for memory allocation failure
160 		return false;
161 	if (s.open(QIODevice::ReadOnly))
162 	{
163 		int byteswrite = 0;
164 		int bytesread = s.read( bb.data(), bb.size() );
165 		while (bytesread > 0)
166 		{
167 			byteswrite = target.writeRawData(bb.data(), bytesread);
168 			copySucceed &= (byteswrite == bytesread);
169 			bytesread = s.read( bb.data(), bb.size() );
170 		}
171 		copySucceed &= (s.error() == QFile::NoError);
172 		s.close();
173 	}
174 	return copySucceed;
175 }
176 
moveFile(const QString & source,const QString & target)177 bool moveFile(const QString& source, const QString& target)
178 {
179 	if (source.isEmpty() || target.isEmpty())
180 		return false;
181 	if (source == target)
182 		return false;
183 	bool moveSucceed = copyFile(source, target);
184 	if (moveSucceed)
185 		moveSucceed &= QFile::remove(source);
186 	return moveSucceed;
187 }
188 
touchFile(const QString & file)189 bool touchFile(const QString& file)
190 {
191 #if defined(_WIN32) && defined(HAVE_UNICODE)
192 	return _wutime((const wchar_t*) file.utf16(), nullptr) == 0;
193 #else
194 	QByteArray fname = file.toLocal8Bit();
195 	return utime(fname.data(), nullptr) == 0;
196 #endif
197 }
198 
199 
fileInPath(const QString & filename)200 bool fileInPath(const QString& filename)
201 {
202 	if (filename.isEmpty())
203 		return false;
204 	QString file = filename.split(' ', Qt::SkipEmptyParts).at(0); //Ignore parameters
205 #if defined(Q_OS_WIN32)
206 	if (QFileInfo(file).suffix().isEmpty())
207 		file += ".exe";
208 #endif
209 
210 	file = QDir::fromNativeSeparators(file);
211 	if (file.indexOf('/') >= 0)
212 	{
213 		//Looks like an absolute path
214 		QFileInfo info(file);
215 		return info.exists();
216 	}
217 
218 	//Get $PATH
219 	QString path;
220 	const QStringList env = QProcess::systemEnvironment();
221 	for (const QString& line : env)
222 	{
223 		if (line.indexOf("PATH") == 0)
224 		{
225 			path = line.mid(5); //Strip "PATH="
226 			break;
227 		}
228 	}
229 
230 	QChar envPathSeparator(ScPaths::envPathSeparator);
231 	const QStringList splitpath = path.split(envPathSeparator, Qt::SkipEmptyParts);
232 	for (const QString& dir : splitpath)
233 	{
234 		QFileInfo info(dir, file);
235 		if (info.exists())
236 			return true;
237 	}
238 	return false;
239 }
240 
getVectorFileFromData(ScribusDoc * doc,QByteArray & data,const QString & ext,double x,double y,double w,double h)241 PageItem* getVectorFileFromData(ScribusDoc *doc, QByteArray &data, const QString& ext, double x, double y, double w, double h)
242 {
243 	PageItem* retObj = nullptr;
244 
245 	QScopedPointer<QTemporaryFile> tempFile(new QTemporaryFile(QDir::tempPath() + "/scribus_temp_XXXXXX." + ext));
246 	if (!tempFile->open())
247 		return nullptr;
248 
249 	QString fileName = getLongPathName(tempFile->fileName());
250 	if (fileName.isEmpty())
251 		return nullptr;
252 
253 	tempFile->write(data);
254 	tempFile->close();
255 
256 	FileLoader *fileLoader = new FileLoader(fileName);
257 	int testResult = fileLoader->testFile();
258 	delete fileLoader;
259 
260 	if (testResult == -1)
261 		return nullptr;
262 
263 	const FileFormat * fmt = LoadSavePlugin::getFormatById(testResult);
264 	if (!fmt)
265 		return nullptr;
266 
267 	doc->m_Selection->clear();
268 	doc->m_Selection->delaySignalsOn();
269 	fmt->setupTargets(doc, nullptr, nullptr, nullptr, &(PrefsManager::instance().appPrefs.fontPrefs.AvailFonts));
270 	fmt->loadFile(fileName, LoadSavePlugin::lfUseCurrentPage|LoadSavePlugin::lfInteractive|LoadSavePlugin::lfScripted);
271 	if (!doc->m_Selection->isEmpty())
272 	{
273 		retObj = doc->groupObjectsSelection();
274 		retObj->setTextFlowMode(PageItem::TextFlowUsesBoundingBox);
275 		retObj->setXYPos(x, y, true);
276 		if ((w >= 0) && (h >= 0))
277 			retObj->setWidthHeight(w, h, true);
278 		retObj->updateClip();
279 		retObj->update();
280 	}
281 	doc->m_Selection->clear();
282 	doc->m_Selection->delaySignalsOff();
283 
284 	return retObj;
285 }
286 
checkFileHash(const QString & directory,const QString & filename,const QString & hashFilename,QCryptographicHash::Algorithm method)287 bool checkFileHash(const QString& directory, const QString& filename, const QString& hashFilename, QCryptographicHash::Algorithm method)
288 {
289 	//In a single directory, make a hash of filename, and compare it to the string for that file in hashFilename
290 	//Assumption is that the hash file only has one line for now
291 	QByteArray ba_hash;
292 	if (!loadRawText(directory + hashFilename, ba_hash))
293 	{
294 		qDebug() << "checkFileHash: loadRawText file unsuccessful";
295 		return false;
296 	}
297 
298 	QFile source(directory + filename);
299 	if (source.open(QIODevice::ReadOnly))
300 	{
301 		ba_hash = ba_hash.simplified();
302 		QList<QByteArray> fileData(ba_hash.split(' '));
303 		QCryptographicHash ch(method);
304 		ch.addData(&source);
305 		source.close();
306 		if (fileData[0] == ch.result().toHex() && fileData[1] == filename)
307 			return true;
308 		qDebug()<<"checkFileHash: checksum failed for"<<directory<<filename;
309 	}
310 	return false;
311 }
312