File indexing completed on 2025-01-19 05:20:19

0001 /*
0002     This file is part of the Okteta Kasten module, made within the KDE community.
0003 
0004     SPDX-FileCopyrightText: 2010 Friedrich W. H. Kossebau <kossebau@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007 */
0008 
0009 #include "bytearrayuuencodingstreamencoder.hpp"
0010 
0011 // lib
0012 #include "../base64/bytearraybase64streamencoder.hpp"
0013 // Okteta core
0014 #include <Okteta/AbstractByteArrayModel>
0015 // KF
0016 #include <KConfigGroup>
0017 #include <KSharedConfig>
0018 #include <KLocalizedString>
0019 // Qt
0020 #include <QTextStream>
0021 
0022 template <>
0023 inline Kasten::UuencodingStreamEncoderSettings::EncodingType
0024 KConfigGroup::readEntry(const char *key,
0025                         const Kasten::UuencodingStreamEncoderSettings::EncodingType &defaultValue) const
0026 {
0027     const QString entry = readEntry(key, QString());
0028     const Kasten::UuencodingStreamEncoderSettings::EncodingType encodingType =
0029         (entry == QLatin1String("Historical")) ?
0030             Kasten::UuencodingStreamEncoderSettings::EncodingType::Historical :
0031         (entry == QLatin1String("Base64")) ?
0032             Kasten::UuencodingStreamEncoderSettings::EncodingType::Base64 :
0033         /* else */                                 defaultValue;
0034     return encodingType;
0035 }
0036 
0037 template <>
0038 inline void KConfigGroup::writeEntry(const char *key,
0039                                      const Kasten::UuencodingStreamEncoderSettings::EncodingType &value,
0040                                      KConfigBase::WriteConfigFlags flags)
0041 {
0042     const QString valueString =
0043         (value == Kasten::UuencodingStreamEncoderSettings::EncodingType::Historical) ?
0044         QLatin1String("Historical") : QLatin1String("Base64");
0045     writeEntry(key, valueString, flags);
0046 }
0047 
0048 namespace Kasten {
0049 
0050 const QString UuencodingStreamEncoderSettings::DefaultFileName = QStringLiteral("okteta-export");
0051 
0052 static inline constexpr char uumapByteHistorical(char byte) { return (byte > 0) ? (byte + 32) : '`'; }
0053 static inline char uumapByteBase64(char byte)     { return base64EncodeMap[(int)byte]; }
0054 
0055 struct UumapEncodeData
0056 {
0057     char (* mapByte)(char);
0058     const char* header;
0059     const char* footer;
0060     const char* paddingData[2];
0061     bool hasLength;
0062 
0063     inline const char* padding(ByteArrayUuencodingStreamEncoder::InputByteIndex index) const
0064     {
0065         return paddingData[static_cast<int>(index) - 1];
0066     }
0067 };
0068 
0069 static constexpr UumapEncodeData historicalUumapEncodeData =
0070 {
0071     &uumapByteHistorical,
0072     "begin",
0073     "\n`\nend\n",
0074     {"``", "`"},
0075     true
0076 };
0077 
0078 static constexpr UumapEncodeData base64UumapEncodeData =
0079 {
0080     &uumapByteBase64,
0081     "begin-base64",
0082     "\n====\n",
0083     {"==", "="},
0084     false
0085 };
0086 
0087 UuencodingStreamEncoderSettings::UuencodingStreamEncoderSettings() = default;
0088 
0089 bool UuencodingStreamEncoderSettings::operator==(const UuencodingStreamEncoderSettings& other) const
0090 {
0091     return (fileName == other.fileName) && (encodingType == other.encodingType);
0092 }
0093 
0094 void UuencodingStreamEncoderSettings::loadConfig(const KConfigGroup& configGroup)
0095 {
0096     fileName = configGroup.readEntry(FileNameConfigKey, DefaultFileName);
0097     encodingType = configGroup.readEntry(EncodingTypeConfigKey, DefaultEncodingType);
0098 }
0099 
0100 void UuencodingStreamEncoderSettings::saveConfig(KConfigGroup& configGroup) const
0101 {
0102     configGroup.writeEntry(FileNameConfigKey, fileName);
0103     configGroup.writeEntry(EncodingTypeConfigKey, encodingType);
0104 }
0105 
0106 
0107 ByteArrayUuencodingStreamEncoder::ByteArrayUuencodingStreamEncoder()
0108     : AbstractByteArrayStreamEncoder(i18nc("name of the encoding target", "Uuencoding"), QStringLiteral("text/x-uuencode"))
0109 {
0110     const KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0111     mSettings.loadConfig(configGroup);
0112 }
0113 
0114 ByteArrayUuencodingStreamEncoder::~ByteArrayUuencodingStreamEncoder() = default;
0115 
0116 void ByteArrayUuencodingStreamEncoder::setSettings(const UuencodingStreamEncoderSettings& settings)
0117 {
0118     if (mSettings == settings) {
0119         return;
0120     }
0121 
0122     mSettings = settings;
0123     KConfigGroup configGroup(KSharedConfig::openConfig(), ConfigGroupId);
0124     mSettings.saveConfig(configGroup);
0125     Q_EMIT settingsChanged();
0126 }
0127 
0128 bool ByteArrayUuencodingStreamEncoder::encodeDataToStream(QIODevice* device,
0129                                                           const ByteArrayView* byteArrayView,
0130                                                           const Okteta::AbstractByteArrayModel* byteArrayModel,
0131                                                           const Okteta::AddressRange& range)
0132 {
0133     Q_UNUSED(byteArrayView);
0134 
0135     bool success = true;
0136 
0137     // encode
0138     QTextStream textStream(device);
0139 
0140     // prepare
0141     InputByteIndex inputByteIndex = InputByteIndex::First;
0142     int inputGroupsPerLine = 0;
0143     unsigned char bitsFromLastByte;
0144 
0145     const UumapEncodeData* encodeData =
0146         (mSettings.encodingType == UuencodingStreamEncoderSettings::EncodingType::Historical) ?
0147             &historicalUumapEncodeData :
0148         /* else */
0149             &base64UumapEncodeData;
0150 
0151     // header
0152     textStream << encodeData->header << " 644 " << mSettings.fileName.toLatin1();
0153 
0154     const int firstLineLength = qMin(range.width(), uuInputLineLength);
0155     if (firstLineLength > 0) {
0156         textStream << '\n';
0157         if (encodeData->hasLength) {
0158             textStream << encodeData->mapByte(firstLineLength);
0159         }
0160     }
0161 
0162     for (Okteta::Address i = range.start(); i <= range.end(); ++i) {
0163         const Okteta::Byte byte = byteArrayModel->byte(i);
0164 
0165         switch (inputByteIndex)
0166         {
0167         case InputByteIndex::First:
0168             // bits 7..2
0169             textStream << encodeData->mapByte(byte >> 2);
0170             // bits 1..0 -> 5..4 for next
0171             bitsFromLastByte = (byte & 0x3) << 4;
0172             inputByteIndex = InputByteIndex::Second;
0173             break;
0174         case InputByteIndex::Second:
0175             // from last and bits 7..4 as 3..0 from this
0176             textStream << encodeData->mapByte(bitsFromLastByte | byte >> 4);
0177             // bits 3..0 -> 5..2 for next
0178             bitsFromLastByte = (byte & 0xf) << 2;
0179             inputByteIndex = InputByteIndex::Third;
0180             break;
0181         case InputByteIndex::Third:
0182             // from last and bits 7..6 as 1..0 from this
0183             textStream << encodeData->mapByte(bitsFromLastByte | byte >> 6);
0184             // bits 5..0
0185             textStream << encodeData->mapByte(byte & 0x3F);
0186             inputByteIndex = InputByteIndex::First;
0187             ++inputGroupsPerLine;
0188             if (inputGroupsPerLine >= maxInputGroupsPerLine && i < range.end()) {
0189                 const int remainsCount = range.end() - i;
0190                 const int nextLineLength = qMin(remainsCount, uuInputLineLength);
0191                 textStream << '\n';
0192                 if (encodeData->hasLength) {
0193                     textStream << encodeData->mapByte(nextLineLength);
0194                 }
0195                 inputGroupsPerLine = 0;
0196             }
0197             break;
0198         }
0199     }
0200 
0201     const bool hasBitsLeft = (inputByteIndex != InputByteIndex::First);
0202     if (hasBitsLeft) {
0203         textStream << encodeData->mapByte(bitsFromLastByte)
0204                    << encodeData->padding(inputByteIndex);
0205     }
0206     // footer
0207     textStream << encodeData->footer;
0208 
0209     return success;
0210 }
0211 
0212 }
0213 
0214 #include "moc_bytearrayuuencodingstreamencoder.cpp"