summaryrefslogtreecommitdiffstats
path: root/libktorrent/torrent/torrentcreator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libktorrent/torrent/torrentcreator.cpp')
-rw-r--r--libktorrent/torrent/torrentcreator.cpp388
1 files changed, 388 insertions, 0 deletions
diff --git a/libktorrent/torrent/torrentcreator.cpp b/libktorrent/torrent/torrentcreator.cpp
new file mode 100644
index 0000000..7b132b8
--- /dev/null
+++ b/libktorrent/torrent/torrentcreator.cpp
@@ -0,0 +1,388 @@
+/***************************************************************************
+ * 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 <qdir.h>
+#include <qfileinfo.h>
+#include <klocale.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 QString & tar,
+ const QStringList & track,
+ Uint32 cs,
+ const QString & name,
+ const QString & 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;
+ QFileInfo 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 QString & dir)
+ {
+ QDir d(target + dir);
+ // first get all files (we ignore symlinks)
+ QStringList dfiles = d.entryList(QDir::Files|QDir::NoSymLinks);
+ Uint32 cnt = 0; // counter to keep track of file index
+ for (QStringList::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
+ QStringList subdirs = d.entryList(QDir::Dirs|QDir::NoSymLinks);
+ for (QStringList::iterator i = subdirs.begin();i != subdirs.end();++i)
+ {
+ if (*i == "." || *i == "..")
+ continue;
+
+ QString sd = dir + *i;
+ if (!sd.endsWith(bt::DirSeparator()))
+ sd += bt::DirSeparator();
+ buildFileList(sd);
+ }
+ }
+
+
+ void TorrentCreator::saveTorrent(const QString & 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("announce"); enc.write(trackers[0]);
+ if (trackers.count() > 1)
+ {
+ enc.write("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("comments");
+ enc.write(comments);
+ }
+ enc.write("created by");enc.write(QString("KTorrent %1").arg(kt::VERSION_STRING));
+ enc.write("creation date");enc.write((Uint64)time(0));
+ enc.write("info");
+ saveInfo(enc);
+ // save the nodes list after the info hash, keys must be sorted !
+ if (decentralized)
+ {
+ //DHT torrent
+ enc.write("nodes");
+ enc.beginList();
+
+ for(int i=0; i < trackers.count(); ++i)
+ {
+ QString 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();
+
+ QFileInfo fi(target);
+ if (fi.isDir())
+ {
+ enc.write("files");
+ enc.beginList();
+ QValueList<TorrentFile>::iterator i = files.begin();
+ while (i != files.end())
+ {
+ saveFile(enc,*i);
+ i++;
+ }
+ enc.end();
+ }
+ else
+ {
+ enc.write("length"); enc.write(bt::FileSize(target));
+ }
+ enc.write("name"); enc.write(name);
+ enc.write("piece length"); enc.write((Uint64)chunk_size);
+ enc.write("pieces"); savePieces(enc);
+ if (priv)
+ {
+ enc.write("private");
+ enc.write((Uint64)1);
+ }
+ enc.end();
+ }
+
+ void TorrentCreator::saveFile(BEncoder & enc,const TorrentFile & file)
+ {
+ enc.beginDict();
+ enc.write("length");enc.write(file.getSize());
+ enc.write("path");
+ enc.beginList();
+ QStringList sl = QStringList::split(bt::DirSeparator(),file.getPath());
+ for (QStringList::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);
+ QValueList<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 QString & data_dir)
+ {
+ QString 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
+ QFileInfo fi = QFileInfo(target);
+
+ QString 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", QString::number(tot_size));
+ st.writeSync();
+
+ tc->init(0,dd + "torrent",dd,odir,QString::null);
+ tc->createFiles();
+ }
+ catch (...)
+ {
+ delete tc;
+ throw;
+ }
+
+ return tc;
+ }
+}