//=============================================================================
//
// 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 <tqfileinfo.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 tqmask " 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 tqmask is a good protection but you have to use it properly ! . [ br ]
Setting the user tqmask 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::tqcontains() to TQString::count(), and TQString::tqcontains() now does the right job
m_uWildCount = m_szUserMask . count ( ' * ' ) ;
# else
m_uWildCount = m_szUserMask . tqcontains ( ' * ' ) ;
# endif
m_uNonWildCount = m_szUserMask . length ( ) - m_uWildCount ;
}
KviSharedFile : : ~ KviSharedFile ( )
{
}
KviSharedFilesManager : : KviSharedFilesManager ( )
: TQObject ( )
{
m_pSharedListDict = new KviPointerHashTable < TQString , KviSharedFileList > ( ) ;
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 < TQString , KviSharedFileList > it ( * m_pSharedListDict ) ;
time_t curTime = time ( 0 ) ;
bool bOtherStuffToCleanup = false ;
//bool bChanged = false;
KviPointerList < TQString > 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 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 tqmask 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 tqmask 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 tqmasks go first....
if ( fo - > tqmaskLength ( ) < o - > tqmaskLength ( ) )
{
// 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 - > tqfind ( f - > name ( ) ) ;
if ( ! l )
{
l = new KviSharedFileList ;
l - > setAutoDelete ( true ) ;
m_pSharedListDict - > tqreplace ( 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 - > tqfind ( szName ) ;
if ( ! l )
{
l = new KviSharedFileList ;
l - > setAutoDelete ( true ) ;
m_pSharedListDict - > tqreplace ( 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 * tqmask , unsigned int uFileSize )
{
KviSharedFileList * l = m_pSharedListDict - > tqfind ( szName ) ;
if ( ! l ) return 0 ;
for ( KviSharedFile * o = l - > first ( ) ; o ; o = l - > next ( ) )
{
bool bMatch ;
if ( tqmask )
{
KviIrcMask umask ( o - > userMask ( ) ) ;
bMatch = tqmask - > 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 - > tqfind ( 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 - > tqfind ( 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 < num ; idx + + )
{
TQString tmp ;
KviTQString : : sprintf ( tmp , " %dFName " , idx ) ;
TQString szName = cfg . readTQStringEntry ( tmp , " " ) ;
KviTQString : : sprintf ( tmp , " %dFilePath " , idx ) ;
TQString szPath = cfg . readTQStringEntry ( tmp , " " ) ;
KviTQString : : sprintf ( tmp , " %dUserMask " , idx ) ;
TQString szMask = cfg . readTQStringEntry ( tmp , " " ) ;
if ( ! szMask . isEmpty ( ) & & ! szPath . isEmpty ( ) & & ! szName . isEmpty ( ) )
addSharedFile ( szName , szPath , szMask , 0 ) ;
}
}
void KviSharedFilesManager : : save ( const TQString & filename )
{
KviConfig cfg ( filename , KviConfig : : Write ) ;
cfg . clear ( ) ;
cfg . setGroup ( " PermanentFileOffers " ) ;
KviPointerHashTableIterator < TQString , 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 )
{
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 ) ;
}