1 /*
2 Copyright (C) 2005-2014 Sergey A. Tachenov
3
4 This file is part of QuaZIP.
5
6 QuaZIP is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 2.1 of the License, or
9 (at your option) any later version.
10
11 QuaZIP is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with QuaZIP. If not, see <http://www.gnu.org/licenses/>.
18
19 See COPYING file for the full LGPL text.
20
21 Original ZIP package is copyrighted by Gilles Vollant and contributors,
22 see quazip/(un)zip.h files for details. Basically it's the zlib license.
23 */
24
25 #include <QFileInfo>
26
27 #include "quazipnewinfo.h"
28
29 #include <string.h>
30
QuaZipNewInfo_setPermissions(QuaZipNewInfo * info,QFile::Permissions perm,bool isDir,bool isSymLink=false)31 static void QuaZipNewInfo_setPermissions(QuaZipNewInfo *info,
32 QFile::Permissions perm, bool isDir, bool isSymLink = false)
33 {
34 quint32 uPerm = isDir ? 0040000 : 0100000;
35
36 if ( isSymLink ) {
37 #ifdef Q_OS_WIN
38 uPerm = 0200000;
39 #else
40 uPerm = 0120000;
41 #endif
42 }
43
44 if ((perm & QFile::ReadOwner) != 0)
45 uPerm |= 0400;
46 if ((perm & QFile::WriteOwner) != 0)
47 uPerm |= 0200;
48 if ((perm & QFile::ExeOwner) != 0)
49 uPerm |= 0100;
50 if ((perm & QFile::ReadGroup) != 0)
51 uPerm |= 0040;
52 if ((perm & QFile::WriteGroup) != 0)
53 uPerm |= 0020;
54 if ((perm & QFile::ExeGroup) != 0)
55 uPerm |= 0010;
56 if ((perm & QFile::ReadOther) != 0)
57 uPerm |= 0004;
58 if ((perm & QFile::WriteOther) != 0)
59 uPerm |= 0002;
60 if ((perm & QFile::ExeOther) != 0)
61 uPerm |= 0001;
62 info->externalAttr = (info->externalAttr & ~0xFFFF0000u) | (uPerm << 16);
63 }
64
65 template<typename FileInfo>
QuaZipNewInfo_init(QuaZipNewInfo & self,const FileInfo & existing)66 void QuaZipNewInfo_init(QuaZipNewInfo &self, const FileInfo &existing)
67 {
68 self.name = existing.name;
69 self.dateTime = existing.dateTime;
70 self.internalAttr = existing.internalAttr;
71 self.externalAttr = existing.externalAttr;
72 self.comment = existing.comment;
73 self.extraLocal = existing.extra;
74 self.extraGlobal = existing.extra;
75 self.uncompressedSize = existing.uncompressedSize;
76 }
77
QuaZipNewInfo(const QuaZipFileInfo & existing)78 QuaZipNewInfo::QuaZipNewInfo(const QuaZipFileInfo &existing)
79 {
80 QuaZipNewInfo_init(*this, existing);
81 }
82
QuaZipNewInfo(const QuaZipFileInfo64 & existing)83 QuaZipNewInfo::QuaZipNewInfo(const QuaZipFileInfo64 &existing)
84 {
85 QuaZipNewInfo_init(*this, existing);
86 }
87
QuaZipNewInfo(const QString & name)88 QuaZipNewInfo::QuaZipNewInfo(const QString& name):
89 name(name), dateTime(QDateTime::currentDateTime()), internalAttr(0), externalAttr(0),
90 uncompressedSize(0)
91 {
92 }
93
QuaZipNewInfo(const QString & name,const QString & file)94 QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file):
95 name(name), internalAttr(0), externalAttr(0), uncompressedSize(0)
96 {
97 QFileInfo info(file);
98 QDateTime lm = info.lastModified();
99 if (!info.exists()) {
100 dateTime = QDateTime::currentDateTime();
101 } else {
102 dateTime = lm;
103 QuaZipNewInfo_setPermissions(this, info.permissions(), info.isDir(), info.isSymLink());
104 }
105 }
106
setFileDateTime(const QString & file)107 void QuaZipNewInfo::setFileDateTime(const QString& file)
108 {
109 QFileInfo info(file);
110 QDateTime lm = info.lastModified();
111 if (info.exists())
112 dateTime = lm;
113 }
114
setFilePermissions(const QString & file)115 void QuaZipNewInfo::setFilePermissions(const QString &file)
116 {
117 QFileInfo info = QFileInfo(file);
118 QFile::Permissions perm = info.permissions();
119 QuaZipNewInfo_setPermissions(this, perm, info.isDir(), info.isSymLink());
120 }
121
setPermissions(QFile::Permissions permissions)122 void QuaZipNewInfo::setPermissions(QFile::Permissions permissions)
123 {
124 QuaZipNewInfo_setPermissions(this, permissions, name.endsWith('/'));
125 }
126
setFileNTFSTimes(const QString & fileName)127 void QuaZipNewInfo::setFileNTFSTimes(const QString &fileName)
128 {
129 QFileInfo fi(fileName);
130 if (!fi.exists()) {
131 qWarning("QuaZipNewInfo::setFileNTFSTimes(): '%s' doesn't exist",
132 fileName.toUtf8().constData());
133 return;
134 }
135 setFileNTFSmTime(fi.lastModified());
136 setFileNTFSaTime(fi.lastRead());
137 setFileNTFScTime(fi.created());
138 }
139
setNTFSTime(QByteArray & extra,const QDateTime & time,int position,int fineTicks)140 static void setNTFSTime(QByteArray &extra, const QDateTime &time, int position,
141 int fineTicks) {
142 int ntfsPos = -1, timesPos = -1;
143 unsigned ntfsLength = 0, ntfsTimesLength = 0;
144 for (int i = 0; i <= extra.size() - 4; ) {
145 unsigned type = static_cast<unsigned>(static_cast<unsigned char>(
146 extra.at(i)))
147 | (static_cast<unsigned>(static_cast<unsigned char>(
148 extra.at(i + 1))) << 8);
149 i += 2;
150 unsigned length = static_cast<unsigned>(static_cast<unsigned char>(
151 extra.at(i)))
152 | (static_cast<unsigned>(static_cast<unsigned char>(
153 extra.at(i + 1))) << 8);
154 i += 2;
155 if (type == QUAZIP_EXTRA_NTFS_MAGIC) {
156 ntfsPos = i - 4; // the beginning of the NTFS record
157 ntfsLength = length;
158 if (length <= 4) {
159 break; // no times in the NTFS record
160 }
161 i += 4; // reserved
162 while (i <= extra.size() - 4) {
163 unsigned tag = static_cast<unsigned>(
164 static_cast<unsigned char>(extra.at(i)))
165 | (static_cast<unsigned>(
166 static_cast<unsigned char>(extra.at(i + 1)))
167 << 8);
168 i += 2;
169 unsigned tagsize = static_cast<unsigned>(
170 static_cast<unsigned char>(extra.at(i)))
171 | (static_cast<unsigned>(
172 static_cast<unsigned char>(extra.at(i + 1)))
173 << 8);
174 i += 2;
175 if (tag == QUAZIP_EXTRA_NTFS_TIME_MAGIC) {
176 timesPos = i - 4; // the beginning of the NTFS times tag
177 ntfsTimesLength = tagsize;
178 break;
179 } else {
180 i += tagsize;
181 }
182 }
183 break; // I ain't going to search for yet another NTFS record!
184 } else {
185 i += length;
186 }
187 }
188 if (ntfsPos == -1) {
189 // No NTFS record, need to create one.
190 ntfsPos = extra.size();
191 ntfsLength = 32;
192 extra.resize(extra.size() + 4 + ntfsLength);
193 // the NTFS record header
194 extra[ntfsPos] = static_cast<char>(QUAZIP_EXTRA_NTFS_MAGIC);
195 extra[ntfsPos + 1] = static_cast<char>(QUAZIP_EXTRA_NTFS_MAGIC >> 8);
196 extra[ntfsPos + 2] = 32; // the 2-byte size in LittleEndian
197 extra[ntfsPos + 3] = 0;
198 // zero the record
199 memset(extra.data() + ntfsPos + 4, 0, 32);
200 timesPos = ntfsPos + 8;
201 // now set the tag data
202 extra[timesPos] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC);
203 extra[timesPos + 1] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC
204 >> 8);
205 // the size:
206 extra[timesPos + 2] = 24;
207 extra[timesPos + 3] = 0;
208 ntfsTimesLength = 24;
209 }
210 if (timesPos == -1) {
211 // No time tag in the NTFS record, need to add one.
212 timesPos = ntfsPos + 4 + ntfsLength;
213 extra.resize(extra.size() + 28);
214 // Now we need to move the rest of the field
215 // (possibly zero bytes, but memmove() is OK with that).
216 // 0 ......... ntfsPos .. ntfsPos + 4 ... timesPos
217 // <some data> <header> <NTFS record> <need-to-move data> <end>
218 memmove(extra.data() + timesPos + 28, extra.data() + timesPos,
219 extra.size() - 28 - timesPos);
220 ntfsLength += 28;
221 // now set the tag data
222 extra[timesPos] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC);
223 extra[timesPos + 1] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC
224 >> 8);
225 // the size:
226 extra[timesPos + 2] = 24;
227 extra[timesPos + 3] = 0;
228 // zero the record
229 memset(extra.data() + timesPos + 4, 0, 24);
230 ntfsTimesLength = 24;
231 }
232 if (ntfsTimesLength < 24) {
233 // Broken times field. OK, this is really unlikely, but just in case...
234 size_t timesEnd = timesPos + 4 + ntfsTimesLength;
235 extra.resize(extra.size() + (24 - ntfsTimesLength));
236 // Move it!
237 // 0 ......... timesPos .... timesPos + 4 .. timesEnd
238 // <some data> <time header> <broken times> <need-to-move data> <end>
239 memmove(extra.data() + timesEnd + (24 - ntfsTimesLength),
240 extra.data() + timesEnd,
241 extra.size() - (24 - ntfsTimesLength) - timesEnd);
242 // Now we have to increase the NTFS record and time tag lengths.
243 ntfsLength += (24 - ntfsTimesLength);
244 ntfsTimesLength = 24;
245 extra[ntfsPos + 2] = static_cast<char>(ntfsLength);
246 extra[ntfsPos + 3] = static_cast<char>(ntfsLength >> 8);
247 extra[timesPos + 2] = static_cast<char>(ntfsTimesLength);
248 extra[timesPos + 3] = static_cast<char>(ntfsTimesLength >> 8);
249 }
250 QDateTime base(QDate(1601, 1, 1), QTime(0, 0), Qt::UTC);
251 #if (QT_VERSION >= 0x040700)
252 quint64 ticks = base.msecsTo(time) * 10000 + fineTicks;
253 #else
254 QDateTime utc = time.toUTC();
255 quint64 ticks = (static_cast<qint64>(base.date().daysTo(utc.date()))
256 * Q_INT64_C(86400000)
257 + static_cast<qint64>(base.time().msecsTo(utc.time())))
258 * Q_INT64_C(10000) + fineTicks;
259 #endif
260 extra[timesPos + 4 + position] = static_cast<char>(ticks);
261 extra[timesPos + 5 + position] = static_cast<char>(ticks >> 8);
262 extra[timesPos + 6 + position] = static_cast<char>(ticks >> 16);
263 extra[timesPos + 7 + position] = static_cast<char>(ticks >> 24);
264 extra[timesPos + 8 + position] = static_cast<char>(ticks >> 32);
265 extra[timesPos + 9 + position] = static_cast<char>(ticks >> 40);
266 extra[timesPos + 10 + position] = static_cast<char>(ticks >> 48);
267 extra[timesPos + 11 + position] = static_cast<char>(ticks >> 56);
268 }
269
setFileNTFSmTime(const QDateTime & mTime,int fineTicks)270 void QuaZipNewInfo::setFileNTFSmTime(const QDateTime &mTime, int fineTicks)
271 {
272 setNTFSTime(extraLocal, mTime, 0, fineTicks);
273 setNTFSTime(extraGlobal, mTime, 0, fineTicks);
274 }
275
setFileNTFSaTime(const QDateTime & aTime,int fineTicks)276 void QuaZipNewInfo::setFileNTFSaTime(const QDateTime &aTime, int fineTicks)
277 {
278 setNTFSTime(extraLocal, aTime, 8, fineTicks);
279 setNTFSTime(extraGlobal, aTime, 8, fineTicks);
280 }
281
setFileNTFScTime(const QDateTime & cTime,int fineTicks)282 void QuaZipNewInfo::setFileNTFScTime(const QDateTime &cTime, int fineTicks)
283 {
284 setNTFSTime(extraLocal, cTime, 16, fineTicks);
285 setNTFSTime(extraGlobal, cTime, 16, fineTicks);
286 }
287