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