diff options
Diffstat (limited to 'src/kvilib/ext/kvi_sharedfiles.cpp')
-rw-r--r-- | src/kvilib/ext/kvi_sharedfiles.cpp | 391 |
1 files changed, 391 insertions, 0 deletions
diff --git a/src/kvilib/ext/kvi_sharedfiles.cpp b/src/kvilib/ext/kvi_sharedfiles.cpp new file mode 100644 index 00000000..65ce0d69 --- /dev/null +++ b/src/kvilib/ext/kvi_sharedfiles.cpp @@ -0,0 +1,391 @@ +//============================================================================= +// +// 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 <qfileinfo.h> + +// 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 QString &szName,const QString &szAbsPath,const QString &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 QString::contains() to QString::count(), and QString::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() +: QObject() +{ + m_pSharedListDict = new KviPointerHashTable<QString,KviSharedFileList>(); + m_pSharedListDict->setAutoDelete(true); + m_pCleanupTimer = new QTimer(); + connect(m_pCleanupTimer,SIGNAL(timeout()),this,SLOT(cleanup())); +} + +KviSharedFilesManager::~KviSharedFilesManager() +{ + if(m_pCleanupTimer->isActive())m_pCleanupTimer->stop(); + delete m_pCleanupTimer; + delete m_pSharedListDict; +} + +void KviSharedFilesManager::cleanup() +{ + KviPointerHashTableIterator<QString,KviSharedFileList> it(*m_pSharedListDict); + time_t curTime = time(0); + + bool bOtherStuffToCleanup = false; + //bool bChanged = false; + + KviPointerList<QString> lDying; + lDying.setAutoDelete(true); + + while(KviSharedFileList * l = it.current()) + { + KviPointerList<KviSharedFile> 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 QString(it.currentKey())); + + ++it; + } + + for(QString * 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 QString &szName,const QString &szAbsPath,const QString &szMask,int timeoutInSecs) +{ + QFileInfo 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",KviQString::toUtf8(szAbsPath).data()); + return 0; + } +} + +KviSharedFile * KviSharedFilesManager::lookupSharedFile(const QString &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 = KviQString::equalCS(o->userMask(),"*!*@*"); + if(bMatch) + { + if(uFileSize > 0) + { + if(uFileSize == o->fileSize())return o; + } else return o; + } + } + + return 0; +} +bool KviSharedFilesManager::removeSharedFile(const QString &szName,const QString &szMask,unsigned int uFileSize) +{ + KviSharedFileList * l = m_pSharedListDict->find(szName); + if(!l)return false; + for(KviSharedFile * o = l->first();o;o = l->next()) + { + if(KviQString::equalCI(szMask,o->userMask())) + { + bool bMatch = uFileSize > 0 ? uFileSize == o->fileSize() : true; + if(bMatch) + { + QString 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 QString &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) + { + QString 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 QString &filename) +{ + KviConfig cfg(filename,KviConfig::Read); + //cfg.clear(); + cfg.setGroup("PermanentFileOffers"); + int num = cfg.readIntEntry("NEntries",0); + for(int idx=0;idx<num;idx++) + { + QString tmp; + KviQString::sprintf(tmp,"%dFName",idx); + QString szName = cfg.readQStringEntry(tmp,""); + KviQString::sprintf(tmp,"%dFilePath",idx); + QString szPath = cfg.readQStringEntry(tmp,""); + KviQString::sprintf(tmp,"%dUserMask",idx); + QString szMask = cfg.readQStringEntry(tmp,""); + if(!szMask.isEmpty() && !szPath.isEmpty() && !szName.isEmpty()) + addSharedFile(szName,szPath,szMask,0); + } +} + +void KviSharedFilesManager::save(const QString &filename) +{ + KviConfig cfg(filename,KviConfig::Write); + cfg.clear(); + cfg.setGroup("PermanentFileOffers"); + + KviPointerHashTableIterator<QString,KviSharedFileList> 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) + { + QString tmp; + KviQString::sprintf(tmp,"%dFName",idx); + cfg.writeEntry(tmp,it.currentKey()); + KviQString::sprintf(tmp,"%dFilePath",idx); + cfg.writeEntry(tmp,o->absFilePath()); + KviQString::sprintf(tmp,"%dUserMask",idx); + cfg.writeEntry(tmp,o->userMask()); + ++idx; + } + } + ++it; + } + cfg.writeEntry("NEntries",idx); +} + |