File indexing completed on 2024-05-12 17:22:25
0001 /* 0002 SPDX-FileCopyrightText: 2003 Rafi Yanai <krusader@users.sf.net> 0003 SPDX-FileCopyrightText: 2003 Shie Erlich <krusader@users.sf.net> 0004 SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org> 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "krarcbasemanager.h" 0010 #include "../../app/krdebuglogger.h" 0011 0012 #include <KArchive/KTar> 0013 0014 #include <QtCore/QStandardPaths> 0015 0016 KrArcBaseManager::AutoDetectParams KrArcBaseManager::autoDetectParams[] = { 0017 {"zip", 0, "PK\x03\x04"}, 0018 {"rar", 0, "Rar!\x1a"}, 0019 {"arj", 0, "\x60\xea"}, 0020 {"rpm", 0, "\xed\xab\xee\xdb"}, 0021 {"ace", 7, "**ACE**"}, 0022 {"bzip2", 0, "\x42\x5a\x68\x39\x31"}, 0023 {"gzip", 0, "\x1f\x8b"}, 0024 {"deb", 0, "!<arch>\ndebian-binary "}, 0025 {"7z", 0, "7z\xbc\xaf\x27\x1c"} /*, 0026 {"xz", 0, "\xfd\x37\x7a\x58\x5a\x00"}*/ 0027 }; 0028 0029 int KrArcBaseManager::autoDetectElems = sizeof(autoDetectParams) / sizeof(AutoDetectParams); 0030 const int KrArcBaseManager::maxLenType = 5; 0031 0032 KrArcBaseManager::KrArcBaseManager() 0033 : krConf("krusaderrc") 0034 , dependGrp(&krConf, "Dependencies") 0035 { 0036 } 0037 0038 QString KrArcBaseManager::fullPathName(const QString &name) 0039 { 0040 // Reminder: If that function is modified, it's important to research if the 0041 // changes must also be applied to `KrServices::fullPathName()` 0042 // and `KrServices::cmdExist()` 0043 0044 // Note: KRFUNC was not used here in order to avoid filling the log with too much information 0045 KRDEBUG(name); 0046 0047 QString supposedName = dependGrp.readEntry(name, QString()); 0048 if (QFileInfo::exists(supposedName)) 0049 return supposedName; 0050 0051 if ((supposedName = QStandardPaths::findExecutable(name)).isEmpty()) 0052 return QString(); 0053 0054 // Because an executable file has been found, its path is remembered 0055 // in order to avoid being searched next time 0056 dependGrp.writeEntry(name, supposedName); 0057 0058 return supposedName; 0059 } 0060 0061 QString KrArcBaseManager::find7zExecutable() 0062 { 0063 KRFUNC; 0064 QString program = fullPathName("7z"); 0065 if (program.isEmpty()) { 0066 KRDEBUG("A 7z program was not found"); 0067 program = fullPathName("7za"); 0068 if (program.isEmpty()) { 0069 KRDEBUG("A 7za program was not found"); 0070 } 0071 } 0072 0073 return program; 0074 } 0075 0076 //! Checks if a returned status ("exit code") of an archiving-related process is OK 0077 /*! 0078 \param arcType A short QString which contains an identifier of the type of the archive. 0079 \param exitCode The returned status ("exit code") of an archiving-related process. 0080 \return If the exit code means that the archiving-related process ended without errors. 0081 */ 0082 bool KrArcBaseManager::checkStatus(const QString &arcType, int exitCode) 0083 { 0084 if (arcType == "zip" || arcType == "rar" || arcType == "7z") 0085 return exitCode == 0 || exitCode == 1; 0086 else if (arcType == "ace" || arcType == "bzip2" || arcType == "lha" || arcType == "rpm" || arcType == "cpio" || arcType == "tar" || arcType == "tarz" 0087 || arcType == "tbz" || arcType == "tgz" || arcType == "arj" || arcType == "deb" || arcType == "tlz" || arcType == "txz") 0088 return exitCode == 0; 0089 else if (arcType == "gzip" || arcType == "lzma" || arcType == "xz") 0090 return exitCode == 0 || exitCode == 2; 0091 else 0092 return exitCode == 0; 0093 } 0094 0095 QString KrArcBaseManager::detectArchive(bool &encrypted, const QString &fileName, bool check7zEncrypted, bool fast) 0096 { 0097 encrypted = false; 0098 0099 QFile arcFile(fileName); 0100 if (arcFile.open(QIODevice::ReadOnly)) { 0101 char buffer[1024]; 0102 long sizeMax = arcFile.read(buffer, sizeof(buffer)); 0103 arcFile.close(); 0104 0105 for (int i = 0; i < autoDetectElems; i++) { 0106 QByteArray detectionString = autoDetectParams[i].detectionString; 0107 int location = autoDetectParams[i].location; 0108 0109 int endPtr = detectionString.length() + location; 0110 if (endPtr > sizeMax) 0111 continue; 0112 0113 int j = 0; 0114 for (; j != detectionString.length(); j++) { 0115 if (detectionString[j] == '?') 0116 continue; 0117 if (buffer[location + j] != detectionString[j]) 0118 break; 0119 } 0120 0121 if (j == detectionString.length()) { 0122 QString type = autoDetectParams[i].type; 0123 if (type == "bzip2" || type == "gzip") { 0124 if (fast) { 0125 if (fileName.endsWith(QLatin1String(".tar.gz"))) 0126 type = "tgz"; 0127 else if (fileName.endsWith(QLatin1String(".tar.bz2"))) 0128 type = "tbz"; 0129 } else { 0130 KTar tapeArchive(fileName); 0131 if (tapeArchive.open(QIODevice::ReadOnly)) { 0132 tapeArchive.close(); 0133 if (type == "gzip") 0134 type = "tgz"; 0135 else if (type == "bzip2") 0136 type = "tbz"; 0137 } 0138 } 0139 } else if (type == "zip") 0140 encrypted = (buffer[6] & 1); 0141 else if (type == "arj") { 0142 if (sizeMax > 4) { 0143 long headerSize = ((unsigned char *)buffer)[2] + 256 * ((unsigned char *)buffer)[3]; 0144 long fileHeader = headerSize + 10; 0145 if (fileHeader + 9 < sizeMax && buffer[fileHeader] == (char)0x60 && buffer[fileHeader + 1] == (char)0xea) 0146 encrypted = (buffer[fileHeader + 8] & 1); 0147 } 0148 } else if (type == "rar") { 0149 if (sizeMax > 13 && buffer[9] == (char)0x73) { 0150 if (buffer[10] & 0x80) { // the header is encrypted? 0151 encrypted = true; 0152 } else { 0153 long offset = 7; 0154 long mainHeaderSize = ((unsigned char *)buffer)[offset + 5] + 256 * ((unsigned char *)buffer)[offset + 6]; 0155 offset += mainHeaderSize; 0156 while (offset + 10 < sizeMax) { 0157 long headerSize = ((unsigned char *)buffer)[offset + 5] + 256 * ((unsigned char *)buffer)[offset + 6]; 0158 bool isDir = (buffer[offset + 7] == '\0') && (buffer[offset + 8] == '\0') && (buffer[offset + 9] == '\0') 0159 && (buffer[offset + 10] == '\0'); 0160 0161 if (buffer[offset + 2] != (char)0x74) 0162 break; 0163 if (!isDir) { 0164 encrypted = (buffer[offset + 3] & 4) != 0; 0165 break; 0166 } 0167 offset += headerSize; 0168 } 0169 } 0170 } 0171 } else if (type == "ace") { 0172 long offset = 0; 0173 long mainHeaderSize = ((unsigned char *)buffer)[offset + 2] + 256 * ((unsigned char *)buffer)[offset + 3] + 4; 0174 offset += mainHeaderSize; 0175 while (offset + 10 < sizeMax) { 0176 long headerSize = ((unsigned char *)buffer)[offset + 2] + 256 * ((unsigned char *)buffer)[offset + 3] + 4; 0177 bool isDir = 0178 (buffer[offset + 11] == '\0') && (buffer[offset + 12] == '\0') && (buffer[offset + 13] == '\0') && (buffer[offset + 14] == '\0'); 0179 0180 if (buffer[offset + 4] != (char)0x01) 0181 break; 0182 if (!isDir) { 0183 encrypted = (buffer[offset + 6] & 64) != 0; 0184 break; 0185 } 0186 offset += headerSize; 0187 } 0188 } else if (type == "7z") { 0189 // encryption check is expensive, check only if it's necessary 0190 if (check7zEncrypted) 0191 checkIf7zIsEncrypted(encrypted, fileName); 0192 } 0193 return type; 0194 } 0195 } 0196 0197 if (sizeMax >= 512) { 0198 /* checking if it's a tar file */ 0199 unsigned checksum = 32 * 8; 0200 char chksum[9]; 0201 for (int i = 0; i != 512; i++) 0202 checksum += ((unsigned char *)buffer)[i]; 0203 for (int i = 148; i != 156; i++) 0204 checksum -= ((unsigned char *)buffer)[i]; 0205 sprintf(chksum, "0%o", checksum); 0206 if (!memcmp(buffer + 148, chksum, strlen(chksum))) { 0207 auto k = strlen(chksum); 0208 for (; k < 8; k++) 0209 if (buffer[148 + k] != 0 && buffer[148 + k] != 32) 0210 break; 0211 if (k == 8) 0212 return "tar"; 0213 } 0214 } 0215 } 0216 0217 if (fileName.endsWith(QLatin1String(".tar.lzma")) || fileName.endsWith(QLatin1String(".tlz"))) { 0218 return "tlz"; 0219 } 0220 if (fileName.endsWith(QLatin1String(".lzma"))) { 0221 return "lzma"; 0222 } 0223 0224 if (fileName.endsWith(QLatin1String(".tar.xz")) || fileName.endsWith(QLatin1String(".txz"))) { 0225 return "txz"; 0226 } 0227 if (fileName.endsWith(QLatin1String(".xz"))) { 0228 return "xz"; 0229 } 0230 0231 return QString(); 0232 } 0233 0234 //! Returns a short identifier of the type of a file, obtained from the mime type of the file 0235 /*! 0236 \param mime The mime type of the file. 0237 \return A short QString which contains an identifier of the type of the file. 0238 */ 0239 QString KrArcBaseManager::getShortTypeFromMime(const QString &mime) 0240 { 0241 // Reminder: If a mime type is added/removed/modified in that 0242 // member function, it's important to research if the type has to 0243 // be added/removed/modified in the `krarc.protocol` file, or 0244 // in `KrArcHandler::KrArcHandler()` 0245 0246 // 7zip files are a not a normal case because their mimetype does not 0247 // follow the norm of other types: zip, tar, lha, ace, arj, etc. 0248 if (mime == "application/x-7z-compressed") 0249 return "7z"; 0250 0251 // If it's a rar file but its mimetype isn't "application/x-rar" 0252 if (mime == "application/x-rar-compressed" || mime == "application/vnd.rar") 0253 return "rar"; 0254 0255 // If it's a cbr file but its mimetype isn't "application/x-cbr" 0256 if (mime == "application/vnd.comicbook-rar") 0257 return "cbr"; 0258 0259 // The short type that will be returned 0260 QString sType = mime; 0261 0262 int lastHyphen = sType.lastIndexOf('-'); 0263 if (lastHyphen != -1) 0264 sType = sType.mid(lastHyphen + 1); 0265 else { 0266 int lastSlash = sType.lastIndexOf('/'); 0267 if (lastSlash != -1) 0268 sType = sType.mid(lastSlash + 1); 0269 } 0270 // The identifier kept short 0271 if (sType.length() > maxLenType) 0272 sType = sType.right(maxLenType); 0273 0274 return sType; 0275 }