/*************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include "importdialog.h" #include #include using namespace bt; namespace kt { ImportDialog::ImportDialog(CoreInterface* core,TQWidget* parent, const char* name, bool modal, WFlags fl) : ImportDlgBase(parent,name, modal,fl),DataCheckerListener(false),core(core) { KURLRequester* r = m_torrent_url; r->setMode(KFile::File|KFile::LocalOnly); r->setFilter("*.torrent|" + i18n("Torrent files") + "\n*|" + i18n("All files")); r = m_data_url; r->setMode(KFile::File|KFile::Directory|KFile::LocalOnly); connect(m_import_btn,TQT_SIGNAL(clicked()),this,TQT_SLOT(onImport())); connect(m_cancel_btn,TQT_SIGNAL(clicked()),this,TQT_SLOT(reject())); m_progress->setEnabled(false); } ImportDialog::~ImportDialog() {} void ImportDialog::progress(Uint32 num,Uint32 total) { m_progress->setTotalSteps(total); m_progress->setProgress(num); } void ImportDialog::status(Uint32 ,Uint32 ) { // don't care } void ImportDialog::finished() { // only used for check in separate thread, so does not apply for the import plugin } void ImportDialog::import(Torrent & tor) { // get the urls KURL tor_url = KURL::fromPathOrURL(m_torrent_url->url()); KURL data_url = KURL::fromPathOrURL(m_data_url->url()); // now we need to check the data DataChecker* dc = 0; if (tor.isMultiFile()) dc = new MultiDataChecker(); else dc = new SingleDataChecker(); try { dc->setListener(this); dc->check(data_url.path(),tor,TQString()); } catch (Error & e) { delete dc; KMessageBox::error(this,i18n("Cannot verify data : %1").tqarg(e.toString()),i18n("Error")); reject(); return; } // find a new torrent dir and make it if necessary TQString tor_dir = core->findNewTorrentDir(); if (!tor_dir.endsWith(bt::DirSeparator())) tor_dir += bt::DirSeparator(); try { if (!bt::Exists(tor_dir)) bt::MakeDir(tor_dir); // write the index file writeIndex(tor_dir + "index",dc->getDownloaded()); // copy the torrent file bt::CopyFile(tor_url.prettyURL(),tor_dir + "torrent"); Uint64 imported = calcImportedBytes(dc->getDownloaded(),tor); // make the cache if (tor.isMultiFile()) { TQValueList dnd_files; bool dnd = false; // first make tor_dir/cache/ TQString cache_dir = tor_dir + "cache" + bt::DirSeparator(); TQString dnd_dir = tor_dir + "dnd" + bt::DirSeparator(); if (!bt::Exists(cache_dir)) MakeDir(cache_dir); if (!bt::Exists(dnd_dir)) MakeDir(dnd_dir); // make all sub symlinks for (Uint32 i = 0;i < tor.getNumFiles();i++) { linkTorFile(cache_dir,dnd_dir,data_url,tor.getFile(i).getPath(),dnd); if (dnd) dnd_files.append(i); dnd = false; } TQString durl = data_url.path(); if (durl.endsWith(bt::DirSeparator())) durl = durl.left(durl.length() - 1); int ds = durl.findRev(bt::DirSeparator()); if (durl.mid(ds+1) == tor.getNameSuggestion()) { durl = durl.left(ds); saveStats(tor_dir + "stats",KURL::fromPathOrURL(durl),imported,false); } else { saveStats(tor_dir + "stats",KURL::fromPathOrURL(durl),imported,true); } saveFileInfo(tor_dir + "file_info",dnd_files); } else { // single file, just symlink the data_url to tor_dir/cache bt::SymLink(data_url.path(),tor_dir + "cache"); TQString durl = data_url.path(); int ds = durl.findRev(bt::DirSeparator()); durl = durl.left(ds); saveStats(tor_dir + "stats",durl,imported,false); } // everything went OK, so load the whole shabang and start downloading core->loadExistingTorrent(tor_dir); } catch (Error & e) { // delete tor_dir bt::Delete(tor_dir,true); delete dc; KMessageBox::error(this,e.toString(),i18n("Error")); reject(); return; } delete dc; accept(); } void ImportDialog::onTorrentGetReult(KIO::Job* j) { if (j->error()) { j->showErrorDialog(this); reject(); } else { KIO::StoredTransferJob* stj = (KIO::StoredTransferJob*)j; Torrent tor; // try to load the torrent try { tor.load(stj->data(),false); } catch (Error & e) { KMessageBox::error(this,i18n("Cannot load the torrent file : %1").tqarg(e.toString()), i18n("Error")); reject(); return; } import(tor); } } void ImportDialog::onImport() { m_progress->setEnabled(true); m_import_btn->setEnabled(false); m_cancel_btn->setEnabled(false); m_torrent_url->setEnabled(false); m_data_url->setEnabled(false); KURL tor_url = KURL::fromPathOrURL(m_torrent_url->url()); if (!tor_url.isLocalFile()) { // download the torrent file KIO::StoredTransferJob* j = KIO::storedGet(tor_url); connect(j,TQT_SIGNAL(result(KIO::Job* )),this,TQT_SLOT(onTorrentGetReult(KIO::Job*))); } else { KURL tor_url = KURL::fromPathOrURL(m_torrent_url->url()); Torrent tor; // try to load the torrent try { tor.load(tor_url.path(),false); } catch (Error & e) { KMessageBox::error(this,i18n("Cannot load the torrent file : %1").tqarg(e.toString()), i18n("Error")); reject(); return; } import(tor); } } void ImportDialog::writeIndex(const TQString & file,const BitSet & chunks) { // first try to open it File fptr; if (!fptr.open(file,"wb")) throw Error(i18n("Cannot open %1 : %2").tqarg(file).tqarg(fptr.errorString())); // write all chunks to the file for (Uint32 i = 0;i < chunks.getNumBits();i++) { if (!chunks.get(i)) continue; // we have the chunk so write a NewChunkHeader struct to the file NewChunkHeader hdr; hdr.index = i; hdr.deprecated = 0; fptr.write(&hdr,sizeof(NewChunkHeader)); } } void ImportDialog::linkTorFile(const TQString & cache_dir,const TQString & dnd_dir, const KURL & data_url,const TQString & fpath,bool & dnd) { TQStringList sl = TQStringList::split(bt::DirSeparator(),fpath); // create all necessary subdirs TQString ctmp = cache_dir; TQString otmp = data_url.path(); if (!otmp.endsWith(bt::DirSeparator())) otmp += bt::DirSeparator(); TQString dtmp = dnd_dir; for (Uint32 i = 0;i < sl.count() - 1;i++) { otmp += sl[i]; ctmp += sl[i]; dtmp += sl[i]; // we need to make the same directory structure in the cache // as the output dir if (!bt::Exists(ctmp)) MakeDir(ctmp); if (!bt::Exists(otmp)) MakeDir(otmp); if (!bt::Exists(dtmp)) MakeDir(dtmp); otmp += bt::DirSeparator(); ctmp += bt::DirSeparator(); dtmp += bt::DirSeparator(); } TQString dfile = otmp + sl.last(); if (!bt::Exists(dfile)) { // when we start the torrent the user will be asked what to do // bt::SymLink(dfile,cache_dir + fpath); dnd = false; } else { // just symlink the existing file bt::SymLink(dfile,cache_dir + fpath); dnd = false; } } void ImportDialog::saveStats(const TQString & stats_file,const KURL & data_url,Uint64 imported,bool custom_output_name) { TQFile fptr(stats_file); if (!fptr.open(IO_WriteOnly)) { Out(SYS_PFI|LOG_IMPORTANT) << "Warning : can't create stats file" << endl; return; } TQTextStream out(&fptr); out << "OUTPUTDIR=" << data_url.path() << ::endl; out << "UPLOADED=0" << ::endl; out << "RUNNING_TIME_DL=0" << ::endl; out << "RUNNING_TIME_UL=0" << ::endl; out << "PRIORITY=0" << ::endl; out << "AUTOSTART=1" << ::endl; if (core->getGlobalMaxShareRatio() > 0) out << TQString("MAX_RATIO=%1").tqarg(core->getGlobalMaxShareRatio(),0,'f',2) << ::endl; out << TQString("IMPORTED=%1").tqarg(imported) << ::endl; if (custom_output_name) out << "CUSTOM_OUTPUT_NAME=1" << endl; } Uint64 ImportDialog::calcImportedBytes(const bt::BitSet & chunks,const Torrent & tor) { Uint64 nb = 0; Uint64 ls = tor.getFileLength() % tor.getChunkSize(); if (ls == 0) ls = tor.getChunkSize(); for (Uint32 i = 0;i < chunks.getNumBits();i++) { if (!chunks.get(i)) continue; if (i == chunks.getNumBits() - 1) nb += ls; else nb += tor.getChunkSize(); } return nb; } void ImportDialog::saveFileInfo(const TQString & file_info_file,TQValueList & dnd) { // saves which TorrentFile's do not need to be downloaded File fptr; if (!fptr.open(file_info_file,"wb")) { Out(SYS_PFI|LOG_IMPORTANT) << "Warning : Can't save chunk_info file : " << fptr.errorString() << endl; return; } ; // first write the number of excluded ones Uint32 tmp = dnd.count(); fptr.write(&tmp,sizeof(Uint32)); // then all the excluded ones for (Uint32 i = 0;i < dnd.count();i++) { tmp = dnd[i]; fptr.write(&tmp,sizeof(Uint32)); } fptr.flush(); } } #include "importdialog.moc"