summaryrefslogtreecommitdiffstats
path: root/src/kvilib/ext/kvi_sharedfiles.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kvilib/ext/kvi_sharedfiles.cpp')
-rw-r--r--src/kvilib/ext/kvi_sharedfiles.cpp391
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);
+}
+