1
//=============================================================================
3
// File : kvi_filetrader.cpp
4
// Creation date : Wed Aug 27 2000 10:33:11 CEST by Szymon Stefanek
6
// This file is part of the KVirc irc client distribution
7
// Copyright (C) 2000-2008 Szymon Stefanek (pragma at kvirc dot net)
9
// This program is FREE software. You can redistribute it and/or
10
// modify it under the terms of the GNU General Public License
11
// as published by the Free Software Foundation; either version 2
12
// of the License, or (at your opinion) any later version.
14
// This program is distributed in the HOPE that it will be USEFUL,
15
// but WITHOUT ANY WARRANTY; without even the implied warranty of
16
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
// See the GNU General Public License for more details.
19
// You should have received a copy of the GNU General Public License
20
// along with this program. If not, write to the Free Software Foundation,
21
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23
//=============================================================================
27
#include "kvi_sharedfiles.h"
29
#include "kvi_config.h"
30
#include "kvi_fileutils.h"
34
// TODO: Match servers that the file requests come from
35
// TODO: Max number of downloads ?
42
Sharing files with KVIrc
46
Automatically sharing your files with other IRC users
50
[big]What is this ?[/big]
51
The "file offers" are a simple way to share your files with other IRC users.[br]
52
Basically , you setup an offer by selecting a local file, choosing a "visible name" for it.
53
Remote users will be able to request you the file and download it automatically by
54
issuing a simple DCC GET request.[br]
56
Each offer refers to an existing file on one of your locally mounted file systems.
57
The offer is given a visible name that the remote users will effectively request.
58
To share the file /usr/arch/mp3/SonataArctica_SingInSilence_Live.mp3 you will add a file offer
59
with /usr/arch/mp3/SonataArctica_SingInSilence_Live.mp3 as real file path , something like
60
"SonataArctica_SingInSilence.mp3". A remote user will then request you a DCC GET SonataArctica_SingInSilence.mp3
61
and KVIrc will automatically send the file.[br]
62
Each file offer has an "user mask" that the requesting remote users must match to
63
obtain the file: *!*@* matches any user, Pragma!*@* matches any user with nickname pragma,
64
*!*@*.omnikron.net matches any user coming from the omnikron.net domain.[br]
65
Each offer can have an expire time: the offer will be automatically removed after
66
a defined number of seconds. An expire time of '0' seconds means that the offer should never expire.[br]
67
If you have two file offers with the same name and different file, the remote user can
68
use an additional "size" parameter in the DCC GET request.[br]
69
[big]Security issues[/big]
70
This is a nice but unsecure method of sharing files.[br]
71
The user mask is a good protection but you have to use it properly!.[br]
72
Setting the user mask to Nick!*@* can be easily exploited (just by making an user disconnect
73
in one of the well known ways and then by using his nickname).[br]
74
On the other side, the remote end must know exactly the visible name of the offer to request
75
and noone but you will tell him that name.[br]
77
Don't share any really important files: this *might* be like putting it on your webpage :D[br]
78
Please don't send complains if someone stoles your /etc/passwd : it is because you have permitted that.[br]
81
KviSharedFile::KviSharedFile(const QString &szName,const QString &szAbsPath,const QString &szUserMask,time_t expireTime,unsigned int uFileSize)
84
m_szAbsFilePath = szAbsPath;
85
m_szUserMask = szUserMask;
86
m_expireTime = expireTime;
87
m_uFileSize = uFileSize;
89
m_uWildCount = m_szUserMask.count('*');
90
m_uNonWildCount = m_szUserMask.length() - m_uWildCount;
93
KviSharedFile::~KviSharedFile()
98
KviSharedFilesManager::KviSharedFilesManager()
101
m_pSharedListDict = new KviPointerHashTable<QString,KviSharedFileList>();
102
m_pSharedListDict->setAutoDelete(true);
103
m_pCleanupTimer = new QTimer();
104
connect(m_pCleanupTimer,SIGNAL(timeout()),this,SLOT(cleanup()));
107
KviSharedFilesManager::~KviSharedFilesManager()
109
if(m_pCleanupTimer->isActive())m_pCleanupTimer->stop();
110
delete m_pCleanupTimer;
111
delete m_pSharedListDict;
114
void KviSharedFilesManager::cleanup()
116
KviPointerHashTableIterator<QString,KviSharedFileList> it(*m_pSharedListDict);
117
time_t curTime = time(0);
119
bool bOtherStuffToCleanup = false;
120
//bool bChanged = false;
122
KviPointerList<QString> lDying;
123
lDying.setAutoDelete(true);
125
while(KviSharedFileList * l = it.current())
127
KviPointerList<KviSharedFile> tmp;
128
tmp.setAutoDelete(false);
129
for(KviSharedFile * o = l->first();o;o = l->next())
131
if(o->expireTime() > 0)
133
if(((int)o->expireTime()) <= ((int)curTime))
138
bOtherStuffToCleanup = true;
142
for(KviSharedFile * fo = tmp.first();fo;fo = tmp.next())
145
emit sharedFileRemoved(fo);
148
lDying.append(new QString(it.currentKey()));
153
for(QString * pDyingKey = lDying.first();pDyingKey;pDyingKey = lDying.next())
154
m_pSharedListDict->remove(*pDyingKey);
156
if(!bOtherStuffToCleanup)m_pCleanupTimer->stop();
157
//if(bChanged)emit sharedFilesChanged();
160
void KviSharedFilesManager::clear()
162
m_pSharedListDict->clear();
163
emit sharedFilesChanged();
166
void KviSharedFilesManager::doInsert(KviSharedFileList * l, KviSharedFile * o)
169
for(KviSharedFile * fo =l->first();fo;fo = l->next())
171
if(o->wildcardCount() > 0)
173
// the new mask has wildcards... if the current one has none, skip it
174
if(fo->wildcardCount() > 0)
176
// the one in the list has wildcards too...
177
// the ones with more non-wild chars go first...
178
if(fo->nonWildcardCount() < o->nonWildcardCount())
180
// ok...the new one has more non-wildcards , insert
184
if(o->nonWildcardCount() == fo->nonWildcardCount())
186
// the same number of non-wildcards
187
// let the number of wildcards decide (it will be eventually equal)
188
if(o->wildcardCount() < fo->wildcardCount())
190
// the new one has less wildcards... goes first
193
} // else the same number of wildcards and non-wildcards...skip
194
} // else the existing one has more non-wildcards...skip
196
} // else the current has no wildcards...skip
198
// the new mask has no wildcards....
199
if(fo->wildcardCount() > 0)
201
// current one has wildcards...insert
205
// the current one has no wildcards...
206
// the longer masks go first....
207
if(fo->maskLength() < o->maskLength())
209
// the current one is shorter than the new one...insert
212
} // else current one is longer...skip
219
void KviSharedFilesManager::addSharedFile(KviSharedFile * f)
221
// First find the list
222
KviSharedFileList * l = m_pSharedListDict->find(f->name());
225
l = new KviSharedFileList;
226
l->setAutoDelete(true);
227
m_pSharedListDict->replace(f->name(),l);
232
if(((int)f->expireTime()) > 0)
234
if(!m_pCleanupTimer->isActive())m_pCleanupTimer->start(60000);
237
emit sharedFileAdded(f);
240
KviSharedFile * KviSharedFilesManager::addSharedFile(const QString &szName,const QString &szAbsPath,const QString &szMask,int timeoutInSecs)
242
QFileInfo inf(szAbsPath);
243
if(inf.exists() && inf.isFile() && inf.isReadable() && (inf.size() > 0))
245
// First find the list
246
KviSharedFileList * l = m_pSharedListDict->find(szName);
249
l = new KviSharedFileList;
250
l->setAutoDelete(true);
251
m_pSharedListDict->replace(szName,l);
255
KviSharedFile * o = new KviSharedFile(szName,szAbsPath,szMask,timeoutInSecs > 0 ? (((int)(time(0))) + timeoutInSecs) : 0,inf.size());
259
if(((int)o->expireTime()) > 0)
261
if(!m_pCleanupTimer->isActive())m_pCleanupTimer->start(60000);
264
emit sharedFileAdded(o);
268
debug("File %s unreadable: can't add offer",KviQString::toUtf8(szAbsPath).data());
273
KviSharedFile * KviSharedFilesManager::lookupSharedFile(const QString &szName,KviIrcMask * mask,unsigned int uFileSize)
275
KviSharedFileList * l = m_pSharedListDict->find(szName);
278
for(KviSharedFile * o = l->first();o;o = l->next())
283
KviIrcMask umask(o->userMask());
284
bMatch = mask->matchedBy(umask);
285
} else bMatch = KviQString::equalCS(o->userMask(),"*!*@*");
290
if(uFileSize == o->fileSize())return o;
297
bool KviSharedFilesManager::removeSharedFile(const QString &szName,const QString &szMask,unsigned int uFileSize)
299
KviSharedFileList * l = m_pSharedListDict->find(szName);
301
for(KviSharedFile * o = l->first();o;o = l->next())
303
if(KviQString::equalCI(szMask,o->userMask()))
305
bool bMatch = uFileSize > 0 ? uFileSize == o->fileSize() : true;
308
QString save = szName; // <-- szName MAY Be a pointer to o->name()
310
if(l->count() == 0)m_pSharedListDict->remove(save);
311
emit sharedFileRemoved(o);
319
bool KviSharedFilesManager::removeSharedFile(const QString &szName,KviSharedFile * off)
321
KviSharedFileList * l = m_pSharedListDict->find(szName);
323
for(KviSharedFile * o = l->first();o;o = l->next())
327
QString save = szName; // <-- szName MAY Be a pointer to o->name()
329
if(l->count() == 0)m_pSharedListDict->remove(save);
330
emit sharedFileRemoved(off);
338
void KviSharedFilesManager::load(const QString &filename)
340
KviConfig cfg(filename,KviConfig::Read);
342
cfg.setGroup("PermanentFileOffers");
343
int num = cfg.readIntEntry("NEntries",0);
344
for(int idx=0;idx<num;idx++)
347
KviQString::sprintf(tmp,"%dFName",idx);
348
QString szName = cfg.readQStringEntry(tmp,"");
349
KviQString::sprintf(tmp,"%dFilePath",idx);
350
QString szPath = cfg.readQStringEntry(tmp,"");
351
KviQString::sprintf(tmp,"%dUserMask",idx);
352
QString szMask = cfg.readQStringEntry(tmp,"");
353
if(!szMask.isEmpty() && !szPath.isEmpty() && !szName.isEmpty())
354
addSharedFile(szName,szPath,szMask,0);
358
void KviSharedFilesManager::save(const QString &filename)
360
KviConfig cfg(filename,KviConfig::Write);
362
cfg.setGroup("PermanentFileOffers");
364
KviPointerHashTableIterator<QString,KviSharedFileList> it(*m_pSharedListDict);
366
while(KviSharedFileList * l = it.current())
368
for(KviSharedFile * o = l->first();o;o = l->next())
370
if(((int)(o->expireTime())) == 0)
373
KviQString::sprintf(tmp,"%dFName",idx);
374
cfg.writeEntry(tmp,it.currentKey());
375
KviQString::sprintf(tmp,"%dFilePath",idx);
376
cfg.writeEntry(tmp,o->absFilePath());
377
KviQString::sprintf(tmp,"%dUserMask",idx);
378
cfg.writeEntry(tmp,o->userMask());
384
cfg.writeEntry("NEntries",idx);