//============================================================================= // // File : kvi_filetrader.cpp // Creation date : Wed Aug 27 2000 10:33:11 CEST by Szymon Stefanek // // This file is part of the KVirc irc client distribution // Copyright (C) 1999-2007 Szymon Stefanek (pragma at kvirc dot net) // // 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 opinion) 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. // //============================================================================= #define __KVILIB__ #include "kvi_sharedfiles.h" #include "kvi_config.h" #include "kvi_fileutils.h" #include // TODO: Match servers that the file requests come from // TODO: Max number of downloads ? // FIXME: MD5SUM ? /* @doc: shared_files @title: Sharing files with KVIrc @type: generic @short: Automatically sharing your files with other IRC users @keyterms: file sharing @body: [big]What is this ?[/big] The "file offers" are a simple way to share your files with other IRC users.[br] Basically , you setup an offer by selecting a local file, choosing a "visible name" for it. Remote users will be able to request you the file and download it automatically by issuing a simple DCC GET request.[br] [big]Details[/big] Each offer refers to an existing file on one of your locally mounted file systems. The offer is given a visible name that the remote users will effectively request. To share the file /usr/arch/mp3/SonataArctica_SingInSilence_Live.mp3 you will add a file offer with /usr/arch/mp3/SonataArctica_SingInSilence_Live.mp3 as real file path , something like "SonataArctica_SingInSilence.mp3". A remote user will then request you a DCC GET SonataArctica_SingInSilence.mp3 and KVIrc will automatically send the file.[br] Each file offer has an "user mask" that the requesting remote users must match to obtain the file: *!*@* matches any user, Pragma!*@* matches any user with nickname pragma, *!*@*.omnikron.net matches any user coming from the omnikron.net domain.[br] Each offer can have an expire time: the offer will be automatically removed after a defined number of seconds. An expire time of '0' seconds means that the offer should never expire.[br] If you have two file offers with the same name and different file, the remote user can use an additional "size" parameter in the DCC GET request.[br] [big]Security issues[/big] This is a nice but unsecure method of sharing files.[br] The user mask is a good protection but you have to use it properly!.[br] Setting the user mask to Nick!*@* can be easily exploited (just by making an user disconnect in one of the well known ways and then by using his nickname).[br] On the other side, the remote end must know exactly the visible name of the offer to request and noone but you will tell him that name.[br] In sum:[br] Don't share any really important files: this *might* be like putting it on your webpage :D[br] Please don't send complains if someone stoles your /etc/passwd : it is because you have permitted that.[br] */ KviSharedFile::KviSharedFile(const TQString &szName,const TQString &szAbsPath,const TQString &szUserMask,time_t expireTime,unsigned int uFileSize) { m_szName = szName; m_szAbsFilePath = szAbsPath; m_szUserMask = szUserMask; m_expireTime = expireTime; m_uFileSize = uFileSize; #ifdef COMPILE_USE_QT4 // QT4ROX: Because they have finally moved the functionality of TQString::contains() to TQString::count(), and TQString::contains() now does the right job m_uWildCount = m_szUserMask.count('*'); #else m_uWildCount = m_szUserMask.contains('*'); #endif m_uNonWildCount = m_szUserMask.length() - m_uWildCount; } KviSharedFile::~KviSharedFile() { } KviSharedFilesManager::KviSharedFilesManager() : TQObject() { m_pSharedListDict = new KviPointerHashTable(); m_pSharedListDict->setAutoDelete(true); m_pCleanupTimer = new TQTimer(); connect(m_pCleanupTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(cleanup())); } KviSharedFilesManager::~KviSharedFilesManager() { if(m_pCleanupTimer->isActive())m_pCleanupTimer->stop(); delete m_pCleanupTimer; delete m_pSharedListDict; } void KviSharedFilesManager::cleanup() { KviPointerHashTableIterator it(*m_pSharedListDict); time_t curTime = time(0); bool bOtherStuffToCleanup = false; //bool bChanged = false; KviPointerList lDying; lDying.setAutoDelete(true); while(KviSharedFileList * l = it.current()) { KviPointerList tmp; tmp.setAutoDelete(false); for(KviSharedFile * o = l->first();o;o = l->next()) { if(o->expireTime() > 0) { if(((int)o->expireTime()) <= ((int)curTime)) { tmp.append(o); //bChanged = true; } else { bOtherStuffToCleanup = true; } } } for(KviSharedFile * fo = tmp.first();fo;fo = tmp.next()) { l->removeRef(fo); emit sharedFileRemoved(fo); } if(l->count() == 0) lDying.append(new TQString(it.currentKey())); ++it; } for(TQString * pDyingKey = lDying.first();pDyingKey;pDyingKey = lDying.next()) m_pSharedListDict->remove(*pDyingKey); if(!bOtherStuffToCleanup)m_pCleanupTimer->stop(); //if(bChanged)emit sharedFilesChanged(); } void KviSharedFilesManager::clear() { m_pSharedListDict->clear(); emit sharedFilesChanged(); } void KviSharedFilesManager::doInsert(KviSharedFileList * l, KviSharedFile * o) { int index = 0; for(KviSharedFile * fo =l->first();fo;fo = l->next()) { if(o->wildcardCount() > 0) { // the new mask has wildcards... if the current one has none, skip it if(fo->wildcardCount() > 0) { // the one in the list has wildcards too... // the ones with more non-wild chars go first... if(fo->nonWildcardCount() < o->nonWildcardCount()) { // ok...the new one has more non-wildcards , insert l->insert(index,o); return; } else { if(o->nonWildcardCount() == fo->nonWildcardCount()) { // the same number of non-wildcards // let the number of wildcards decide (it will be eventually equal) if(o->wildcardCount() < fo->wildcardCount()) { // the new one has less wildcards... goes first l->insert(index,o); return; } // else the same number of wildcards and non-wildcards...skip } // else the existing one has more non-wildcards...skip } } // else the current has no wildcards...skip } else { // the new mask has no wildcards.... if(fo->wildcardCount() > 0) { // current one has wildcards...insert l->insert(index,o); return; } // the current one has no wildcards... // the longer masks go first.... if(fo->maskLength() < o->maskLength()) { // the current one is shorter than the new one...insert l->insert(index,o); return; } // else current one is longer...skip } index++; } l->append(o); } void KviSharedFilesManager::addSharedFile(KviSharedFile * f) { // First find the list KviSharedFileList * l = m_pSharedListDict->find(f->name()); if(!l) { l = new KviSharedFileList; l->setAutoDelete(true); m_pSharedListDict->replace(f->name(),l); } doInsert(l,f); if(((int)f->expireTime()) > 0) { if(!m_pCleanupTimer->isActive())m_pCleanupTimer->start(60000); } emit sharedFileAdded(f); } KviSharedFile * KviSharedFilesManager::addSharedFile(const TQString &szName,const TQString &szAbsPath,const TQString &szMask,int timeoutInSecs) { TQFileInfo inf(szAbsPath); if(inf.exists() && inf.isFile() && inf.isReadable() && (inf.size() > 0)) { // First find the list KviSharedFileList * l = m_pSharedListDict->find(szName); if(!l) { l = new KviSharedFileList; l->setAutoDelete(true); m_pSharedListDict->replace(szName,l); } // Now insert KviSharedFile * o = new KviSharedFile(szName,szAbsPath,szMask,timeoutInSecs > 0 ? (((int)(time(0))) + timeoutInSecs) : 0,inf.size()); doInsert(l,o); if(((int)o->expireTime()) > 0) { if(!m_pCleanupTimer->isActive())m_pCleanupTimer->start(60000); } emit sharedFileAdded(o); return o; } else { debug("File %s unreadable: can't add offer",KviTQString::toUtf8(szAbsPath).data()); return 0; } } KviSharedFile * KviSharedFilesManager::lookupSharedFile(const TQString &szName,KviIrcMask * mask,unsigned int uFileSize) { KviSharedFileList * l = m_pSharedListDict->find(szName); if(!l)return 0; for(KviSharedFile * o = l->first();o;o = l->next()) { bool bMatch; if(mask) { KviIrcMask umask(o->userMask()); bMatch = mask->matchedBy(umask); } else bMatch = KviTQString::equalCS(o->userMask(),"*!*@*"); if(bMatch) { if(uFileSize > 0) { if(uFileSize == o->fileSize())return o; } else return o; } } return 0; } bool KviSharedFilesManager::removeSharedFile(const TQString &szName,const TQString &szMask,unsigned int uFileSize) { KviSharedFileList * l = m_pSharedListDict->find(szName); if(!l)return false; for(KviSharedFile * o = l->first();o;o = l->next()) { if(KviTQString::equalCI(szMask,o->userMask())) { bool bMatch = uFileSize > 0 ? uFileSize == o->fileSize() : true; if(bMatch) { TQString save = szName; // <-- szName MAY Be a pointer to o->name() l->removeRef(o); if(l->count() == 0)m_pSharedListDict->remove(save); emit sharedFileRemoved(o); return true; } } } return false; } bool KviSharedFilesManager::removeSharedFile(const TQString &szName,KviSharedFile * off) { KviSharedFileList * l = m_pSharedListDict->find(szName); if(!l)return false; for(KviSharedFile * o = l->first();o;o = l->next()) { if(off == o) { TQString save = szName; // <-- szName MAY Be a pointer to o->name() l->removeRef(o); if(l->count() == 0)m_pSharedListDict->remove(save); emit sharedFileRemoved(off); return true; } } return false; } void KviSharedFilesManager::load(const TQString &filename) { KviConfig cfg(filename,KviConfig::Read); //cfg.clear(); cfg.setGroup("PermanentFileOffers"); int num = cfg.readIntEntry("NEntries",0); for(int idx=0;idx it(*m_pSharedListDict); int idx = 0; while(KviSharedFileList * l = it.current()) { for(KviSharedFile * o = l->first();o;o = l->next()) { if(((int)(o->expireTime())) == 0) { TQString tmp; KviTQString::sprintf(tmp,"%dFName",idx); cfg.writeEntry(tmp,it.currentKey()); KviTQString::sprintf(tmp,"%dFilePath",idx); cfg.writeEntry(tmp,o->absFilePath()); KviTQString::sprintf(tmp,"%dUserMask",idx); cfg.writeEntry(tmp,o->userMask()); ++idx; } } ++it; } cfg.writeEntry("NEntries",idx); }