You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
10 KiB
389 lines
10 KiB
/***************************************************************************
|
|
* Copyright (C) 2005 by Joris Guisson *
|
|
* joris.guisson@gmail.com *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program 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 General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
|
|
***************************************************************************/
|
|
#include <tqdir.h>
|
|
#include <tqfileinfo.h>
|
|
#include <tdelocale.h>
|
|
#include <time.h>
|
|
#include <util/error.h>
|
|
#include <ktversion.h>
|
|
#include "torrentcontrol.h"
|
|
#include "torrentcreator.h"
|
|
#include "bencoder.h"
|
|
#include <util/file.h>
|
|
#include <util/sha1hash.h>
|
|
#include <util/fileops.h>
|
|
#include <util/log.h>
|
|
#include <util/array.h>
|
|
#include <util/functions.h>
|
|
#include "globals.h"
|
|
#include "chunkmanager.h"
|
|
#include "statsfile.h"
|
|
|
|
namespace bt
|
|
{
|
|
|
|
TorrentCreator::TorrentCreator(const TQString & tar,
|
|
const TQStringList & track,
|
|
Uint32 cs,
|
|
const TQString & name,
|
|
const TQString & comments,bool priv, bool decentralized)
|
|
: target(tar),trackers(track),chunk_size(cs),
|
|
name(name),comments(comments),cur_chunk(0),priv(priv),tot_size(0), decentralized(decentralized)
|
|
{
|
|
this->chunk_size *= 1024;
|
|
TQFileInfo fi(target);
|
|
if (fi.isDir())
|
|
{
|
|
if (!this->target.endsWith(bt::DirSeparator()))
|
|
this->target += bt::DirSeparator();
|
|
|
|
tot_size = 0;
|
|
buildFileList("");
|
|
num_chunks = tot_size / chunk_size;
|
|
if (tot_size % chunk_size > 0)
|
|
num_chunks++;
|
|
last_size = tot_size % chunk_size;
|
|
Out() << "Tot Size : " << tot_size << endl;
|
|
}
|
|
else
|
|
{
|
|
tot_size = bt::FileSize(target);
|
|
num_chunks = tot_size / chunk_size;
|
|
if (tot_size % chunk_size > 0)
|
|
num_chunks++;
|
|
last_size = tot_size % chunk_size;
|
|
Out() << "Tot Size : " << tot_size << endl;
|
|
}
|
|
|
|
if (last_size == 0)
|
|
last_size = chunk_size;
|
|
|
|
Out() << "Num Chunks : " << num_chunks << endl;
|
|
Out() << "Chunk Size : " << chunk_size << endl;
|
|
Out() << "Last Size : " << last_size << endl;
|
|
}
|
|
|
|
|
|
TorrentCreator::~TorrentCreator()
|
|
{}
|
|
|
|
void TorrentCreator::buildFileList(const TQString & dir)
|
|
{
|
|
TQDir d(target + dir);
|
|
// first get all files (we ignore symlinks)
|
|
TQStringList dfiles = d.entryList(TQDir::Files|TQDir::NoSymLinks);
|
|
Uint32 cnt = 0; // counter to keep track of file index
|
|
for (TQStringList::iterator i = dfiles.begin();i != dfiles.end();++i)
|
|
{
|
|
// add a TorrentFile to the list
|
|
Uint64 fs = bt::FileSize(target + dir + *i);
|
|
TorrentFile f(cnt,dir + *i,tot_size,fs,chunk_size);
|
|
files.append(f);
|
|
// update total size
|
|
tot_size += fs;
|
|
cnt++;
|
|
}
|
|
|
|
// now for each subdir do a buildFileList
|
|
TQStringList subdirs = d.entryList(TQDir::Dirs|TQDir::NoSymLinks);
|
|
for (TQStringList::iterator i = subdirs.begin();i != subdirs.end();++i)
|
|
{
|
|
if (*i == "." || *i == "..")
|
|
continue;
|
|
|
|
TQString sd = dir + *i;
|
|
if (!sd.endsWith(bt::DirSeparator()))
|
|
sd += bt::DirSeparator();
|
|
buildFileList(sd);
|
|
}
|
|
}
|
|
|
|
|
|
void TorrentCreator::saveTorrent(const TQString & url)
|
|
{
|
|
File fptr;
|
|
if (!fptr.open(url,"wb"))
|
|
throw Error(i18n("Cannot open file %1: %2").arg(url).arg(fptr.errorString()));
|
|
|
|
BEncoder enc(&fptr);
|
|
enc.beginDict(); // top dict
|
|
|
|
if(!decentralized)
|
|
{
|
|
enc.write(TQString("announce")); enc.write(trackers[0]);
|
|
if (trackers.count() > 1)
|
|
{
|
|
enc.write(TQString("announce-list"));
|
|
enc.beginList();
|
|
enc.beginList();
|
|
for (Uint32 i = 0;i < trackers.count();i++)
|
|
enc.write(trackers[i]);
|
|
enc.end();
|
|
enc.end();
|
|
|
|
}
|
|
}
|
|
|
|
|
|
if (comments.length() > 0)
|
|
{
|
|
enc.write(TQString("comments"));
|
|
enc.write(comments);
|
|
}
|
|
enc.write(TQString("created by"));enc.write(TQString("KTorrent %1").arg(kt::VERSION_STRING));
|
|
enc.write(TQString("creation date"));enc.write((Uint64)time(0));
|
|
enc.write(TQString("info"));
|
|
saveInfo(enc);
|
|
// save the nodes list after the info hash, keys must be sorted !
|
|
if (decentralized)
|
|
{
|
|
//DHT torrent
|
|
enc.write(TQString("nodes"));
|
|
enc.beginList();
|
|
|
|
for(int i=0; i < trackers.count(); ++i)
|
|
{
|
|
TQString t = trackers[i];
|
|
enc.beginList();
|
|
enc.write(t.section(',',0,0));
|
|
enc.write((Uint32)t.section(',',1,1).toInt());
|
|
enc.end();
|
|
}
|
|
enc.end();
|
|
}
|
|
|
|
enc.end();
|
|
}
|
|
|
|
void TorrentCreator::saveInfo(BEncoder & enc)
|
|
{
|
|
enc.beginDict();
|
|
|
|
TQFileInfo fi(target);
|
|
if (fi.isDir())
|
|
{
|
|
enc.write(TQString("files"));
|
|
enc.beginList();
|
|
TQValueList<TorrentFile>::iterator i = files.begin();
|
|
while (i != files.end())
|
|
{
|
|
saveFile(enc,*i);
|
|
i++;
|
|
}
|
|
enc.end();
|
|
}
|
|
else
|
|
{
|
|
enc.write(TQString("length")); enc.write(bt::FileSize(target));
|
|
}
|
|
enc.write(TQString("name")); enc.write(name);
|
|
enc.write(TQString("piece length")); enc.write((Uint64)chunk_size);
|
|
enc.write(TQString("pieces")); savePieces(enc);
|
|
if (priv)
|
|
{
|
|
enc.write(TQString("private"));
|
|
enc.write((Uint64)1);
|
|
}
|
|
enc.end();
|
|
}
|
|
|
|
void TorrentCreator::saveFile(BEncoder & enc,const TorrentFile & file)
|
|
{
|
|
enc.beginDict();
|
|
enc.write(TQString("length"));enc.write(file.getSize());
|
|
enc.write(TQString("path"));
|
|
enc.beginList();
|
|
TQStringList sl = TQStringList::split(bt::DirSeparator(),file.getPath());
|
|
for (TQStringList::iterator i = sl.begin();i != sl.end();i++)
|
|
enc.write(*i);
|
|
enc.end();
|
|
enc.end();
|
|
}
|
|
|
|
void TorrentCreator::savePieces(BEncoder & enc)
|
|
{
|
|
if (hashes.empty())
|
|
while (!calculateHash())
|
|
;
|
|
|
|
Array<Uint8> big_hash(num_chunks*20);
|
|
for (Uint32 i = 0;i < num_chunks;++i)
|
|
{
|
|
memcpy(big_hash+(20*i),hashes[i].getData(),20);
|
|
}
|
|
enc.write(big_hash,num_chunks*20);
|
|
}
|
|
|
|
bool TorrentCreator::calcHashSingle()
|
|
{
|
|
Array<Uint8> buf(chunk_size);
|
|
File fptr;
|
|
if (!fptr.open(target,"rb"))
|
|
throw Error(i18n("Cannot open file %1: %2")
|
|
.arg(target).arg(fptr.errorString()));
|
|
|
|
Uint32 s = cur_chunk != num_chunks - 1 ? chunk_size : last_size;
|
|
fptr.seek(File::BEGIN,(Int64)cur_chunk*chunk_size);
|
|
|
|
fptr.read(buf,s);
|
|
SHA1Hash h = SHA1Hash::generate(buf,s);
|
|
hashes.append(h);
|
|
cur_chunk++;
|
|
return cur_chunk >= num_chunks;
|
|
}
|
|
|
|
bool TorrentCreator::calcHashMulti()
|
|
{
|
|
Uint32 s = cur_chunk != num_chunks - 1 ? chunk_size : last_size;
|
|
// first find the file(s) the chunk lies in
|
|
Array<Uint8> buf(s);
|
|
TQValueList<TorrentFile> file_list;
|
|
Uint32 i = 0;
|
|
while (i < files.size())
|
|
{
|
|
const TorrentFile & tf = files[i];
|
|
if (cur_chunk >= tf.getFirstChunk() && cur_chunk <= tf.getLastChunk())
|
|
{
|
|
file_list.append(tf);
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
Uint32 read = 0;
|
|
for (i = 0;i < file_list.count();i++)
|
|
{
|
|
const TorrentFile & f = file_list[i];
|
|
File fptr;
|
|
if (!fptr.open(target + f.getPath(),"rb"))
|
|
{
|
|
throw Error(i18n("Cannot open file %1: %2")
|
|
.arg(f.getPath()).arg(fptr.errorString()));
|
|
}
|
|
|
|
// first calculate offset into file
|
|
// only the first file can have an offset
|
|
// the following files will start at the beginning
|
|
Uint64 off = 0;
|
|
if (i == 0)
|
|
off = f.fileOffset(cur_chunk,chunk_size);
|
|
|
|
Uint32 to_read = 0;
|
|
// then the amount of data we can read from this file
|
|
if (file_list.count() == 1)
|
|
to_read = s;
|
|
else if (i == 0)
|
|
to_read = f.getLastChunkSize();
|
|
else if (i == file_list.count() - 1)
|
|
to_read = s - read;
|
|
else
|
|
to_read = f.getSize();
|
|
|
|
// read part of data
|
|
fptr.seek(File::BEGIN,(Int64)off);
|
|
fptr.read(buf + read,to_read);
|
|
read += to_read;
|
|
}
|
|
|
|
// generate hash
|
|
SHA1Hash h = SHA1Hash::generate(buf,s);
|
|
hashes.append(h);
|
|
|
|
cur_chunk++;
|
|
// Out() << "=============================================" << endl;
|
|
return cur_chunk >= num_chunks;
|
|
}
|
|
|
|
bool TorrentCreator::calculateHash()
|
|
{
|
|
if (cur_chunk >= num_chunks)
|
|
return true;
|
|
if (files.empty())
|
|
return calcHashSingle();
|
|
else
|
|
return calcHashMulti();
|
|
}
|
|
|
|
TorrentControl* TorrentCreator::makeTC(const TQString & data_dir)
|
|
{
|
|
TQString dd = data_dir;
|
|
if (!dd.endsWith(bt::DirSeparator()))
|
|
dd += bt::DirSeparator();
|
|
|
|
// make data dir if necessary
|
|
if (!bt::Exists(dd))
|
|
bt::MakeDir(dd);
|
|
|
|
// save the torrent
|
|
saveTorrent(dd + "torrent");
|
|
// write full index file
|
|
File fptr;
|
|
if (!fptr.open(dd + "index","wb"))
|
|
throw Error(i18n("Cannot create index file: %1").arg(fptr.errorString()));
|
|
|
|
for (Uint32 i = 0;i < num_chunks;i++)
|
|
{
|
|
NewChunkHeader hdr;
|
|
hdr.index = i;
|
|
fptr.write(&hdr,sizeof(NewChunkHeader));
|
|
}
|
|
fptr.close();
|
|
|
|
// now create the torrentcontrol object
|
|
TorrentControl* tc = new TorrentControl();
|
|
try
|
|
{
|
|
// get the parent dir of target
|
|
TQFileInfo fi = TQFileInfo(target);
|
|
|
|
TQString odir;
|
|
StatsFile st(dd + "stats");
|
|
if (fi.fileName() == name)
|
|
{
|
|
st.write("OUTPUTDIR", fi.dirPath(true));
|
|
odir = fi.dirPath(true);
|
|
}
|
|
else
|
|
{
|
|
st.write("CUSTOM_OUTPUT_NAME","1");
|
|
st.write("OUTPUTDIR", target);
|
|
odir = target;
|
|
}
|
|
st.write("UPLOADED", "0");
|
|
st.write("RUNNING_TIME_DL","0");
|
|
st.write("RUNNING_TIME_UL","0");
|
|
st.write("PRIORITY", "0");
|
|
st.write("AUTOSTART", "1");
|
|
st.write("IMPORTED", TQString::number(tot_size));
|
|
st.writeSync();
|
|
|
|
tc->init(0,dd + "torrent",dd,odir,TQString());
|
|
tc->createFiles();
|
|
}
|
|
catch (...)
|
|
{
|
|
delete tc;
|
|
throw;
|
|
}
|
|
|
|
return tc;
|
|
}
|
|
}
|