/*************************************************************************** * 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 #include #include #include #include #include #include "torrentcontrol.h" #include "torrentcreator.h" #include "bencoder.h" #include #include #include #include #include #include #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::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 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 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 buf(s); TQValueList 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; } }