1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the examples of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:BSD$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** BSD License Usage
18 ** Alternatively, you may use this file under the terms of the BSD license
19 ** as follows:
20 **
21 ** "Redistribution and use in source and binary forms, with or without
22 ** modification, are permitted provided that the following conditions are
23 ** met:
24 **   * Redistributions of source code must retain the above copyright
25 **     notice, this list of conditions and the following disclaimer.
26 **   * Redistributions in binary form must reproduce the above copyright
27 **     notice, this list of conditions and the following disclaimer in
28 **     the documentation and/or other materials provided with the
29 **     distribution.
30 **   * Neither the name of The Qt Company Ltd nor the names of its
31 **     contributors may be used to endorse or promote products derived
32 **     from this software without specific prior written permission.
33 **
34 **
35 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46 **
47 ** $QT_END_LICENSE$
48 **
49 ****************************************************************************/
50 
51 #include "bencodeparser.h"
52 #include "metainfo.h"
53 
54 #include <QDateTime>
55 #include <QMetaType>
56 #include <QString>
57 
MetaInfo()58 MetaInfo::MetaInfo()
59 {
60     clear();
61 }
62 
clear()63 void MetaInfo::clear()
64 {
65     errString = "Unknown error";
66     content.clear();
67     infoData.clear();
68     metaInfoMultiFiles.clear();
69     metaInfoAnnounce.clear();
70     metaInfoAnnounceList.clear();
71     metaInfoCreationDate = QDateTime();
72     metaInfoComment.clear();
73     metaInfoCreatedBy.clear();
74     metaInfoName.clear();
75     metaInfoPieceLength = 0;
76     metaInfoSha1Sums.clear();
77 }
78 
parse(const QByteArray & data)79 bool MetaInfo::parse(const QByteArray &data)
80 {
81     clear();
82     content = data;
83 
84     BencodeParser parser;
85     if (!parser.parse(content)) {
86         errString = parser.errorString();
87         return false;
88     }
89 
90     infoData = parser.infoSection();
91 
92     QMap<QByteArray, QVariant> dict = parser.dictionary();
93     if (!dict.contains("info"))
94         return false;
95 
96     QMap<QByteArray, QVariant> info = qvariant_cast<Dictionary>(dict.value("info"));
97 
98     if (info.contains("files")) {
99         metaInfoFileForm = MultiFileForm;
100 
101         QList<QVariant> files = info.value("files").toList();
102 
103         for (int i = 0; i < files.size(); ++i) {
104             const QMap<QByteArray, QVariant> file = qvariant_cast<Dictionary>(files.at(i));
105             const QList<QVariant> pathElements = file.value("path").toList();
106             QByteArray path;
107             for (const QVariant &p : pathElements) {
108                 if (!path.isEmpty())
109                     path += '/';
110                 path += p.toByteArray();
111             }
112 
113             MetaInfoMultiFile multiFile;
114             multiFile.length = file.value("length").toLongLong();
115             multiFile.path = QString::fromUtf8(path);
116             multiFile.md5sum = file.value("md5sum").toByteArray();
117             metaInfoMultiFiles << multiFile;
118         }
119 
120         metaInfoName = QString::fromUtf8(info.value("name").toByteArray());
121         metaInfoPieceLength = info.value("piece length").toInt();
122         QByteArray pieces = info.value("pieces").toByteArray();
123         for (int i = 0; i < pieces.size(); i += 20)
124             metaInfoSha1Sums << pieces.mid(i, 20);
125     } else if (info.contains("length")) {
126         metaInfoFileForm = SingleFileForm;
127         metaInfoSingleFile.length = info.value("length").toLongLong();
128         metaInfoSingleFile.md5sum = info.value("md5sum").toByteArray();
129         metaInfoSingleFile.name = QString::fromUtf8(info.value("name").toByteArray());
130         metaInfoSingleFile.pieceLength = info.value("piece length").toInt();
131 
132         QByteArray pieces = info.value("pieces").toByteArray();
133         for (int i = 0; i < pieces.size(); i += 20)
134             metaInfoSingleFile.sha1Sums << pieces.mid(i, 20);
135     }
136 
137     metaInfoAnnounce = QString::fromUtf8(dict.value("announce").toByteArray());
138 
139     if (dict.contains("announce-list")) {
140         // ### unimplemented
141     }
142 
143     if (dict.contains("creation date"))
144         metaInfoCreationDate.setSecsSinceEpoch(dict.value("creation date").toInt());
145     if (dict.contains("comment"))
146         metaInfoComment = QString::fromUtf8(dict.value("comment").toByteArray());
147     if (dict.contains("created by"))
148         metaInfoCreatedBy = QString::fromUtf8(dict.value("created by").toByteArray());
149 
150     return true;
151 }
152 
infoValue() const153 QByteArray MetaInfo::infoValue() const
154 {
155     return infoData;
156 }
157 
errorString() const158 QString MetaInfo::errorString() const
159 {
160     return errString;
161 }
162 
fileForm() const163 MetaInfo::FileForm MetaInfo::fileForm() const
164 {
165     return metaInfoFileForm;
166 }
167 
announceUrl() const168 QString MetaInfo::announceUrl() const
169 {
170     return metaInfoAnnounce;
171 }
172 
announceList() const173 QStringList MetaInfo::announceList() const
174 {
175     return metaInfoAnnounceList;
176 }
177 
creationDate() const178 QDateTime MetaInfo::creationDate() const
179 {
180     return metaInfoCreationDate;
181 }
182 
comment() const183 QString MetaInfo::comment() const
184 {
185     return metaInfoComment;
186 }
187 
createdBy() const188 QString MetaInfo::createdBy() const
189 {
190     return metaInfoCreatedBy;
191 }
192 
singleFile() const193 MetaInfoSingleFile MetaInfo::singleFile() const
194 {
195     return metaInfoSingleFile;
196 }
197 
multiFiles() const198 QList<MetaInfoMultiFile> MetaInfo::multiFiles() const
199 {
200     return metaInfoMultiFiles;
201 }
202 
name() const203 QString MetaInfo::name() const
204 {
205     return metaInfoName;
206 }
207 
pieceLength() const208 int MetaInfo::pieceLength() const
209 {
210     return metaInfoPieceLength;
211 }
212 
sha1Sums() const213 QList<QByteArray> MetaInfo::sha1Sums() const
214 {
215     return metaInfoSha1Sums;
216 }
217 
totalSize() const218 qint64 MetaInfo::totalSize() const
219 {
220     if (fileForm() == SingleFileForm)
221         return singleFile().length;
222 
223     qint64 size = 0;
224     for (const MetaInfoMultiFile &file : metaInfoMultiFiles)
225         size += file.length;
226     return size;
227 }
228