File indexing completed on 2025-01-26 04:24:54

0001 /*
0002 Copyright (C) 2005-2014 Sergey A. Tachenov
0003 
0004 This file is part of QuaZIP.
0005 
0006 QuaZIP is free software: you can redistribute it and/or modify
0007 it under the terms of the GNU Lesser General Public License as published by
0008 the Free Software Foundation, either version 2.1 of the License, or
0009 (at your option) any later version.
0010 
0011 QuaZIP is distributed in the hope that it will be useful,
0012 but WITHOUT ANY WARRANTY; without even the implied warranty of
0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014 GNU Lesser General Public License for more details.
0015 
0016 You should have received a copy of the GNU Lesser General Public License
0017 along with QuaZIP.  If not, see <http://www.gnu.org/licenses/>.
0018 
0019 See COPYING file for the full LGPL text.
0020 
0021 Original ZIP package is copyrighted by Gilles Vollant and contributors,
0022 see quazip/(un)zip.h files for details. Basically it's the zlib license.
0023 */
0024 
0025 #include <QFileInfo>
0026 
0027 #include "quazipnewinfo.h"
0028 
0029 #include <string.h>
0030 
0031 static void QuaZipNewInfo_setPermissions(QuaZipNewInfo *info,
0032         QFile::Permissions perm, bool isDir, bool isSymLink = false)
0033 {
0034     quint32 uPerm = isDir ? 0040000 : 0100000;
0035 
0036     if ( isSymLink ) {
0037 #ifdef Q_OS_WIN
0038         uPerm = 0200000;
0039 #else
0040         uPerm = 0120000;
0041 #endif
0042     }
0043 
0044     if ((perm & QFile::ReadOwner) != 0)
0045         uPerm |= 0400;
0046     if ((perm & QFile::WriteOwner) != 0)
0047         uPerm |= 0200;
0048     if ((perm & QFile::ExeOwner) != 0)
0049         uPerm |= 0100;
0050     if ((perm & QFile::ReadGroup) != 0)
0051         uPerm |= 0040;
0052     if ((perm & QFile::WriteGroup) != 0)
0053         uPerm |= 0020;
0054     if ((perm & QFile::ExeGroup) != 0)
0055         uPerm |= 0010;
0056     if ((perm & QFile::ReadOther) != 0)
0057         uPerm |= 0004;
0058     if ((perm & QFile::WriteOther) != 0)
0059         uPerm |= 0002;
0060     if ((perm & QFile::ExeOther) != 0)
0061         uPerm |= 0001;
0062     info->externalAttr = (info->externalAttr & ~0xFFFF0000u) | (uPerm << 16);
0063 }
0064 
0065 template<typename FileInfo>
0066 void QuaZipNewInfo_init(QuaZipNewInfo &self, const FileInfo &existing)
0067 {
0068     self.name = existing.name;
0069     self.dateTime = existing.dateTime;
0070     self.internalAttr = existing.internalAttr;
0071     self.externalAttr = existing.externalAttr;
0072     self.comment = existing.comment;
0073     self.extraLocal = existing.extra;
0074     self.extraGlobal = existing.extra;
0075     self.uncompressedSize = existing.uncompressedSize;
0076 }
0077 
0078 QuaZipNewInfo::QuaZipNewInfo(const QuaZipFileInfo &existing)
0079 {
0080     QuaZipNewInfo_init(*this, existing);
0081 }
0082 
0083 QuaZipNewInfo::QuaZipNewInfo(const QuaZipFileInfo64 &existing)
0084 {
0085     QuaZipNewInfo_init(*this, existing);
0086 }
0087 
0088 QuaZipNewInfo::QuaZipNewInfo(const QString& name):
0089   name(name), dateTime(QDateTime::currentDateTime()), internalAttr(0), externalAttr(0),
0090   uncompressedSize(0)
0091 {
0092 }
0093 
0094 QuaZipNewInfo::QuaZipNewInfo(const QString& name, const QString& file):
0095   name(name), internalAttr(0), externalAttr(0), uncompressedSize(0)
0096 {
0097   QFileInfo info(file);
0098   QDateTime lm = info.lastModified();
0099   if (!info.exists()) {
0100     dateTime = QDateTime::currentDateTime();
0101   } else {
0102     dateTime = lm;
0103     QuaZipNewInfo_setPermissions(this, info.permissions(), info.isDir(), info.isSymLink());
0104   }
0105 }
0106 
0107 void QuaZipNewInfo::setFileDateTime(const QString& file)
0108 {
0109   QFileInfo info(file);
0110   QDateTime lm = info.lastModified();
0111   if (info.exists())
0112     dateTime = lm;
0113 }
0114 
0115 void QuaZipNewInfo::setFilePermissions(const QString &file)
0116 {
0117     QFileInfo info = QFileInfo(file);
0118     QFile::Permissions perm = info.permissions();
0119     QuaZipNewInfo_setPermissions(this, perm, info.isDir(), info.isSymLink());
0120 }
0121 
0122 void QuaZipNewInfo::setPermissions(QFile::Permissions permissions)
0123 {
0124     QuaZipNewInfo_setPermissions(this, permissions, name.endsWith('/'));
0125 }
0126 
0127 void QuaZipNewInfo::setFileNTFSTimes(const QString &fileName)
0128 {
0129     QFileInfo fi(fileName);
0130     if (!fi.exists()) {
0131         qWarning("QuaZipNewInfo::setFileNTFSTimes(): '%s' doesn't exist",
0132                  fileName.toUtf8().constData());
0133         return;
0134     }
0135     setFileNTFSmTime(fi.lastModified());
0136     setFileNTFSaTime(fi.lastRead());
0137     setFileNTFScTime(fi.created());
0138 }
0139 
0140 static void setNTFSTime(QByteArray &extra, const QDateTime &time, int position,
0141                         int fineTicks) {
0142     int ntfsPos = -1, timesPos = -1;
0143     unsigned ntfsLength = 0, ntfsTimesLength = 0;
0144     for (int i = 0; i <= extra.size() - 4; ) {
0145         unsigned type = static_cast<unsigned>(static_cast<unsigned char>(
0146                                                   extra.at(i)))
0147                 | (static_cast<unsigned>(static_cast<unsigned char>(
0148                                                   extra.at(i + 1))) << 8);
0149         i += 2;
0150         unsigned length = static_cast<unsigned>(static_cast<unsigned char>(
0151                                                   extra.at(i)))
0152                 | (static_cast<unsigned>(static_cast<unsigned char>(
0153                                                   extra.at(i + 1))) << 8);
0154         i += 2;
0155         if (type == QUAZIP_EXTRA_NTFS_MAGIC) {
0156             ntfsPos = i - 4; // the beginning of the NTFS record
0157             ntfsLength = length;
0158             if (length <= 4) {
0159                 break; // no times in the NTFS record
0160             }
0161             i += 4; // reserved
0162             while (i <= extra.size() - 4) {
0163                 unsigned tag = static_cast<unsigned>(
0164                             static_cast<unsigned char>(extra.at(i)))
0165                         | (static_cast<unsigned>(
0166                                static_cast<unsigned char>(extra.at(i + 1)))
0167                            << 8);
0168                 i += 2;
0169                 unsigned tagsize = static_cast<unsigned>(
0170                             static_cast<unsigned char>(extra.at(i)))
0171                         | (static_cast<unsigned>(
0172                                static_cast<unsigned char>(extra.at(i + 1)))
0173                            << 8);
0174                 i += 2;
0175                 if (tag == QUAZIP_EXTRA_NTFS_TIME_MAGIC) {
0176                     timesPos = i - 4; // the beginning of the NTFS times tag
0177                     ntfsTimesLength = tagsize;
0178                     break;
0179                 } else {
0180                     i += tagsize;
0181                 }
0182             }
0183             break; // I ain't going to search for yet another NTFS record!
0184         } else {
0185             i += length;
0186         }
0187     }
0188     if (ntfsPos == -1) {
0189         // No NTFS record, need to create one.
0190         ntfsPos = extra.size();
0191         ntfsLength = 32;
0192         extra.resize(extra.size() + 4 + ntfsLength);
0193         // the NTFS record header
0194         extra[ntfsPos] = static_cast<char>(QUAZIP_EXTRA_NTFS_MAGIC);
0195         extra[ntfsPos + 1] = static_cast<char>(QUAZIP_EXTRA_NTFS_MAGIC >> 8);
0196         extra[ntfsPos + 2] = 32; // the 2-byte size in LittleEndian
0197         extra[ntfsPos + 3] = 0;
0198         // zero the record
0199         memset(extra.data() + ntfsPos + 4, 0, 32);
0200         timesPos = ntfsPos + 8;
0201         // now set the tag data
0202         extra[timesPos] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC);
0203         extra[timesPos + 1] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC
0204                                                >> 8);
0205         // the size:
0206         extra[timesPos + 2] = 24;
0207         extra[timesPos + 3] = 0;
0208         ntfsTimesLength = 24;
0209     }
0210     if (timesPos == -1) {
0211         // No time tag in the NTFS record, need to add one.
0212         timesPos = ntfsPos + 4 + ntfsLength;
0213         extra.resize(extra.size() + 28);
0214         // Now we need to move the rest of the field
0215         // (possibly zero bytes, but memmove() is OK with that).
0216         // 0 ......... ntfsPos .. ntfsPos + 4   ... timesPos
0217         // <some data> <header>   <NTFS record>     <need-to-move data>    <end>
0218         memmove(extra.data() + timesPos + 28, extra.data() + timesPos,
0219                 extra.size() - 28 - timesPos);
0220         ntfsLength += 28;
0221         // now set the tag data
0222         extra[timesPos] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC);
0223         extra[timesPos + 1] = static_cast<char>(QUAZIP_EXTRA_NTFS_TIME_MAGIC
0224                                                >> 8);
0225         // the size:
0226         extra[timesPos + 2] = 24;
0227         extra[timesPos + 3] = 0;
0228         // zero the record
0229         memset(extra.data() + timesPos + 4, 0, 24);
0230         ntfsTimesLength = 24;
0231     }
0232     if (ntfsTimesLength < 24) {
0233         // Broken times field. OK, this is really unlikely, but just in case...
0234         size_t timesEnd = timesPos + 4 + ntfsTimesLength;
0235         extra.resize(extra.size() + (24 - ntfsTimesLength));
0236         // Move it!
0237         // 0 ......... timesPos .... timesPos + 4 .. timesEnd
0238         // <some data> <time header> <broken times> <need-to-move data> <end>
0239         memmove(extra.data() + timesEnd + (24 - ntfsTimesLength),
0240                 extra.data() + timesEnd,
0241                 extra.size() - (24 - ntfsTimesLength) - timesEnd);
0242         // Now we have to increase the NTFS record and time tag lengths.
0243         ntfsLength += (24 - ntfsTimesLength);
0244         ntfsTimesLength = 24;
0245         extra[ntfsPos + 2] = static_cast<char>(ntfsLength);
0246         extra[ntfsPos + 3] = static_cast<char>(ntfsLength >> 8);
0247         extra[timesPos + 2] = static_cast<char>(ntfsTimesLength);
0248         extra[timesPos + 3] = static_cast<char>(ntfsTimesLength >> 8);
0249     }
0250     QDateTime base(QDate(1601, 1, 1), QTime(0, 0), Qt::UTC);
0251 #if (QT_VERSION >= 0x040700)
0252     quint64 ticks = base.msecsTo(time) * 10000 + fineTicks;
0253 #else
0254     QDateTime utc = time.toUTC();
0255     quint64 ticks = (static_cast<qint64>(base.date().daysTo(utc.date()))
0256             * Q_INT64_C(86400000)
0257             + static_cast<qint64>(base.time().msecsTo(utc.time())))
0258         * Q_INT64_C(10000) + fineTicks;
0259 #endif
0260     extra[timesPos + 4 + position] = static_cast<char>(ticks);
0261     extra[timesPos + 5 + position] = static_cast<char>(ticks >> 8);
0262     extra[timesPos + 6 + position] = static_cast<char>(ticks >> 16);
0263     extra[timesPos + 7 + position] = static_cast<char>(ticks >> 24);
0264     extra[timesPos + 8 + position] = static_cast<char>(ticks >> 32);
0265     extra[timesPos + 9 + position] = static_cast<char>(ticks >> 40);
0266     extra[timesPos + 10 + position] = static_cast<char>(ticks >> 48);
0267     extra[timesPos + 11 + position] = static_cast<char>(ticks >> 56);
0268 }
0269 
0270 void QuaZipNewInfo::setFileNTFSmTime(const QDateTime &mTime, int fineTicks)
0271 {
0272     setNTFSTime(extraLocal, mTime, 0, fineTicks);
0273     setNTFSTime(extraGlobal, mTime, 0, fineTicks);
0274 }
0275 
0276 void QuaZipNewInfo::setFileNTFSaTime(const QDateTime &aTime, int fineTicks)
0277 {
0278     setNTFSTime(extraLocal, aTime, 8, fineTicks);
0279     setNTFSTime(extraGlobal, aTime, 8, fineTicks);
0280 }
0281 
0282 void QuaZipNewInfo::setFileNTFScTime(const QDateTime &cTime, int fineTicks)
0283 {
0284     setNTFSTime(extraLocal, cTime, 16, fineTicks);
0285     setNTFSTime(extraGlobal, cTime, 16, fineTicks);
0286 }