File indexing completed on 2024-05-12 05:54:58

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 }