File indexing completed on 2025-01-05 04:37:08

0001 /*
0002     SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 #include "bdecoder.h"
0007 #include "bnode.h"
0008 #include <klocalizedstring.h>
0009 #include <util/error.h>
0010 #include <util/log.h>
0011 
0012 namespace bt
0013 {
0014 BDecoder::BDecoder(const Uint8 *ptr, Uint32 size, bool verbose, Uint32 off)
0015     : data(QByteArray::fromRawData((const char *)ptr, size))
0016     , pos(off)
0017     , verbose(verbose)
0018     , level(0)
0019 {
0020 }
0021 
0022 BDecoder::BDecoder(const QByteArray &data, bool verbose, Uint32 off)
0023     : data(data)
0024     , pos(off)
0025     , verbose(verbose)
0026     , level(0)
0027 {
0028 }
0029 
0030 BDecoder::~BDecoder()
0031 {
0032 }
0033 
0034 BNode *BDecoder::decode()
0035 {
0036     if (pos >= (Uint32)data.size())
0037         return nullptr;
0038 
0039     if (data[pos] == 'd') {
0040         return parseDict();
0041     } else if (data[pos] == 'l') {
0042         return parseList();
0043     } else if (data[pos] == 'i') {
0044         return parseInt();
0045     } else if (data[pos] >= '0' && data[pos] <= '9') {
0046         return parseString();
0047     } else {
0048         throw Error(i18n("Illegal token: %1", data[pos]));
0049     }
0050 }
0051 
0052 BDictNode *BDecoder::decodeDict()
0053 {
0054     BNode *n = nullptr;
0055     try {
0056         n = decode();
0057         if (n && n->getType() == BNode::DICT)
0058             return (BDictNode *)n;
0059 
0060         delete n;
0061     } catch (...) {
0062         delete n;
0063         throw;
0064     }
0065 
0066     return nullptr;
0067 }
0068 
0069 BListNode *BDecoder::decodeList()
0070 {
0071     BNode *n = nullptr;
0072     try {
0073         n = decode();
0074         if (n && n->getType() == BNode::LIST)
0075             return (BListNode *)n;
0076 
0077         delete n;
0078     } catch (...) {
0079         delete n;
0080         throw;
0081     }
0082 
0083     return nullptr;
0084 }
0085 
0086 BDictNode *BDecoder::parseDict()
0087 {
0088     Uint32 off = pos;
0089     // we're now entering a dictionary
0090     BDictNode *curr = new BDictNode(off);
0091     pos++;
0092     debugMsg(QString("DICT"));
0093     level++;
0094     try {
0095         while (pos < (Uint32)data.size() && data[pos] != 'e') {
0096             debugMsg(QString("Key : "));
0097             BNode *kn = decode();
0098             BValueNode *k = dynamic_cast<BValueNode *>(kn);
0099             if (!k || k->data().getType() != Value::STRING) {
0100                 delete kn;
0101                 throw Error(i18n("Decode error"));
0102             }
0103 
0104             QByteArray key = k->data().toByteArray();
0105             delete kn;
0106 
0107             BNode *value = decode();
0108             if (!value)
0109                 throw Error(i18n("Decode error"));
0110 
0111             curr->insert(key, value);
0112         }
0113         pos++;
0114     } catch (...) {
0115         delete curr;
0116         throw;
0117     }
0118     level--;
0119     debugMsg(QString("END"));
0120     curr->setLength(pos - off);
0121     return curr;
0122 }
0123 
0124 BListNode *BDecoder::parseList()
0125 {
0126     Uint32 off = pos;
0127     debugMsg(QString("LIST"));
0128     level++;
0129     BListNode *curr = new BListNode(off);
0130     pos++;
0131     try {
0132         while (pos < (Uint32)data.size() && data[pos] != 'e') {
0133             BNode *n = decode();
0134             if (n)
0135                 curr->append(n);
0136         }
0137         pos++;
0138     } catch (...) {
0139         delete curr;
0140         throw;
0141     }
0142     level--;
0143     debugMsg(QString("END"));
0144     curr->setLength(pos - off);
0145     return curr;
0146 }
0147 
0148 BValueNode *BDecoder::parseInt()
0149 {
0150     Uint32 off = pos;
0151     pos++;
0152     QString n;
0153     // look for e and add everything between i and e to n
0154     while (pos < (Uint32)data.size() && data[pos] != 'e') {
0155         n += data[pos];
0156         pos++;
0157     }
0158 
0159     // check if we aren't at the end of the data
0160     if (pos >= (Uint32)data.size()) {
0161         throw Error(i18n("Unexpected end of input"));
0162     }
0163 
0164     // try to decode the int
0165     bool ok = true;
0166     int val = 0;
0167     val = n.toInt(&ok);
0168     if (ok) {
0169         pos++;
0170         debugMsg(QStringLiteral("INT = %1").arg(val));
0171         BValueNode *vn = new BValueNode(Value(val), off);
0172         vn->setLength(pos - off);
0173         return vn;
0174     } else {
0175         Int64 bi = 0LL;
0176         bi = n.toLongLong(&ok);
0177         if (!ok)
0178             throw Error(i18n("Cannot convert %1 to an int", n));
0179 
0180         pos++;
0181         debugMsg(QStringLiteral("INT64 = %1").arg(n));
0182         BValueNode *vn = new BValueNode(Value(bi), off);
0183         vn->setLength(pos - off);
0184         return vn;
0185     }
0186 }
0187 
0188 BValueNode *BDecoder::parseString()
0189 {
0190     const Uint32 off = pos;
0191     // string are encoded 4:spam (length:string)
0192 
0193     // first get length by looking for the :
0194     while (pos < (Uint32)data.size() && data[pos] != ':') {
0195         pos++;
0196     }
0197     // check if we aren't at the end of the data
0198     if (pos >= (Uint32)data.size()) {
0199         throw Error(i18n("Unexpected end of input"));
0200     }
0201 
0202     // try to decode length
0203     bool ok = true;
0204     int len = 0;
0205     // This is an optimized version of QByteArray::fromRawData(data.constData() + off, pos - off).toInt(&ok)
0206     const char *start = data.constData() + off;
0207     const char *end = start + pos - off;
0208     while (start < end) {
0209         int n = *start++ - '0';
0210         if (n < 0 || n > 9) {
0211             ok = false;
0212             break;
0213         }
0214         len = (len << 3) + (len << 1) + n;
0215     }
0216 
0217     if (!ok || len < 0) {
0218         throw Error(i18n("Cannot convert %1 to an int", QString::fromUtf8(data.constData() + off, pos - off)));
0219     }
0220     // move pos to the first part of the string
0221     pos++;
0222     if (pos + len > (Uint32)data.size())
0223         throw Error(i18n("Torrent is incomplete."));
0224 
0225     const QByteArray arr(data.constData() + pos, len);
0226     pos += len;
0227     // read the string into n
0228 
0229     // pos should be positioned right after the string
0230     BValueNode *vn = new BValueNode(Value(arr), off);
0231     vn->setLength(pos - off);
0232     if (verbose) {
0233         if (arr.size() < 200)
0234             debugMsg(QStringLiteral("STRING ") + QString::fromUtf8(arr));
0235         else
0236             debugMsg(QStringLiteral("STRING really long string"));
0237     }
0238     return vn;
0239 }
0240 
0241 void BDecoder::debugMsg(const QString &msg)
0242 {
0243     if (!verbose)
0244         return;
0245 
0246     Log &log = Out(SYS_GEN | LOG_DEBUG);
0247     for (int i = 0; i < level; i++)
0248         log << "-";
0249 
0250     log << msg << endl;
0251 }
0252 
0253 }