blob: 4302b539d69a3cf3b5102102f256b79f32758656 [file] [log] [blame]
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
* Copyright (c) 2016 Regents of the University of California.
*
* This file is part of the nTorrent codebase.
*
* nTorrent is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received copies of the GNU General Public License and GNU Lesser
* General Public License along with nTorrent, e.g., in COPYING.md file. If not, see
* <http://www.gnu.org/licenses/>.
*
* See AUTHORS for complete list of nTorrent authors and contributors.
*/
#include "torrent-file.hpp"
#include <algorithm>
#include <boost/range/adaptors.hpp>
namespace ndn {
namespace ntorrent {
BOOST_CONCEPT_ASSERT((WireEncodable<TorrentFile>));
BOOST_CONCEPT_ASSERT((WireDecodable<TorrentFile>));
static_assert(std::is_base_of<Data::Error, TorrentFile::Error>::value,
"TorrentFile::Error should inherit from Data::Error");
TorrentFile::TorrentFile(const Name& torrentFileName,
const Name& torrentFilePtr,
const Name& commonPrefix,
const std::vector<ndn::Name>& catalog)
: Data(torrentFileName)
, m_commonPrefix(commonPrefix)
, m_torrentFilePtr(torrentFilePtr)
, m_catalog(catalog)
{
}
TorrentFile::TorrentFile(const Name& torrentFileName,
const Name& commonPrefix,
const std::vector<ndn::Name>& catalog)
: Data(torrentFileName)
, m_commonPrefix(commonPrefix)
, m_catalog(catalog)
{
}
TorrentFile::TorrentFile(const Block& block)
{
this->wireDecode(block);
}
const Name&
TorrentFile::getName() const
{
return Data::getName();
}
const Name&
TorrentFile::getCommonPrefix() const
{
return m_commonPrefix;
}
void
TorrentFile::createSuffixCatalog()
{
for (auto i = m_catalog.begin() ; i != m_catalog.end(); ++i) {
m_suffixCatalog.push_back((*i).getSubName(m_commonPrefix.size()));
}
}
shared_ptr<Name>
TorrentFile::getTorrentFilePtr() const
{
if (this->hasTorrentFilePtr()) {
return make_shared<Name>(m_torrentFilePtr);
}
return nullptr;
}
void
TorrentFile::constructLongNames()
{
for (auto i = m_suffixCatalog.begin(); i != m_suffixCatalog.end(); ++i) {
Name commonPrefix = m_commonPrefix;
m_catalog.push_back(commonPrefix.append((*i)));
}
}
template<encoding::Tag TAG>
size_t
TorrentFile::encodeContent(EncodingImpl<TAG>& encoder) const
{
// TorrentFileContent ::= CONTENT-TYPE TLV-LENGTH
// Suffix+
// CommonPrefix
// TorrentFilePtr?
// Suffix ::= NAME-TYPE TLV-LENGTH
// Name
// CommonPrefix ::= NAME-TYPE TLV-LENGTH
// Name
// TorrentFilePtr ::= NAME-TYPE TLV-LENGTH
// Name
size_t totalLength = 0;
for (const auto& name : m_suffixCatalog | boost::adaptors::reversed) {
size_t fileManifestSuffixLength = 0;
fileManifestSuffixLength += name.wireEncode(encoder);
totalLength += fileManifestSuffixLength;
}
totalLength += m_commonPrefix.wireEncode(encoder);
if (!m_torrentFilePtr.empty()) {
size_t torrentFilePtrLength = 0;
torrentFilePtrLength += m_torrentFilePtr.wireEncode(encoder);
totalLength += torrentFilePtrLength;
}
totalLength += encoder.prependVarNumber(totalLength);
totalLength += encoder.prependVarNumber(tlv::Content);
return totalLength;
}
bool
TorrentFile::erase(const Name& name)
{
auto found = std::find(m_catalog.begin(), m_catalog.end(), name);
if (found != m_catalog.end()) {
m_catalog.erase(found);
return true;
}
return false;
}
void
TorrentFile::encodeContent()
{
onChanged();
EncodingEstimator estimator;
size_t estimatedSize = encodeContent(estimator);
EncodingBuffer buffer(estimatedSize, 0);
encodeContent(buffer);
setContentType(tlv::ContentType_Blob);
setContent(buffer.block());
}
void
TorrentFile::decodeContent()
{
// TorrentFileContent ::= CONTENT-TYPE TLV-LENGTH
// Suffix+
// CommonPrefix
// TorrentFilePtr?
// Suffix ::= NAME-TYPE TLV-LENGTH
// Name
// CommonPrefix ::= NAME-TYPE TLV-LENGTH
// Name
// TorrentFilePtr ::= NAME-TYPE TLV-LENGTH
// Name
if (getContentType() != tlv::ContentType_Blob) {
BOOST_THROW_EXCEPTION(Error("Expected Content Type Blob"));
}
const Block& content = Data::getContent();
content.parse();
// Check whether there is a TorrentFilePtr
auto element = content.elements_begin();
if (content.elements_end() == element) {
BOOST_THROW_EXCEPTION(Error(".Torrent-file with empty content"));
}
element->parse();
Name name(*element);
if (name.empty())
BOOST_THROW_EXCEPTION(Error("Empty name included in the .torrent-file"));
if (name.get(name.size() - 3) == name::Component(".torrent-file")) {
m_torrentFilePtr = name;
++element;
m_commonPrefix = Name(*element);
if (m_commonPrefix.empty()) {
BOOST_THROW_EXCEPTION(Error("Common prefix cannot be empty"));
}
}
else {
m_commonPrefix = name;
}
element++;
for (; element != content.elements_end(); ++element) {
element->parse();
Name fileManifestSuffix(*element);
if (fileManifestSuffix.empty())
BOOST_THROW_EXCEPTION(Error("Empty manifest file name included in the .torrent-file"));
this->insertToSuffixCatalog(fileManifestSuffix);
}
if (m_suffixCatalog.size() == 0) {
BOOST_THROW_EXCEPTION(Error(".Torrent-file with empty catalog of file manifest names"));
}
}
void
TorrentFile::wireDecode(const Block& wire)
{
m_catalog.clear();
m_suffixCatalog.clear();
Data::wireDecode(wire);
this->decodeContent();
this->constructLongNames();
}
const Block&
TorrentFile::wireEncode()
{
this->createSuffixCatalog();
this->encodeContent();
m_suffixCatalog.clear();
return Data::wireEncode();
}
} // namespace ntorrent
} // namespace ndn