www.pudn.com > BTSERVER.rar > tracker.cpp
/*** * * RainbowBT Beta 7.7 - A C++ BitTorrent Tracker * Copyright (C) 2003 Trevor Hogan * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * ***/ // Trinity Edition Modified Source File #ifndef WIN32 #include#include #endif #include "bnbt.h" #include "atom.h" #include "bencode.h" #include "config.h" #include "md5.h" #include "server.h" #include "sort.h" #include "tracker.h" #include "util.h" map gmapMime; CTracker :: CTracker( ) { m_mtxQueued.Initialize( ); m_strAllowedDir = CFG_GetString( "allowed_dir", string( ) ); m_strUploadDir = CFG_GetString( "RainbowBT_upload_dir", string( ) ); m_strExternalTorrentDir = CFG_GetString( "RainbowBT_external_torrent_dir", string( ) ); m_strArchiveDir = CFG_GetString( "RainbowBT_archive_dir", string( ) ); m_strFileDir = CFG_GetString( "RainbowBT_file_dir", string( ) ); if( !m_strAllowedDir.empty( ) && m_strAllowedDir[m_strAllowedDir.size( ) - 1] != PATH_SEP ) m_strAllowedDir += PATH_SEP; if( !m_strUploadDir.empty( ) && m_strUploadDir[m_strUploadDir.size( ) - 1] != PATH_SEP ) m_strUploadDir += PATH_SEP; if( !m_strExternalTorrentDir.empty( ) && m_strExternalTorrentDir[m_strExternalTorrentDir.size( ) - 1] != '/' ) m_strExternalTorrentDir += '/'; if( !m_strArchiveDir.empty( ) && m_strArchiveDir[m_strArchiveDir.size( ) - 1] != PATH_SEP ) m_strArchiveDir += PATH_SEP; if( !m_strFileDir.empty( ) && m_strFileDir[m_strFileDir.size( ) - 1] != PATH_SEP ) m_strFileDir += PATH_SEP; m_strDFile = CFG_GetString( "dfile", "dstate.RainbowBT" ); m_strCommentsFile = CFG_GetString( "RainbowBT_comments_file", string( ) ); m_strTagFile = CFG_GetString( "RainbowBT_tag_file", "tags.RainbowBT" ); m_strUsersFile = CFG_GetString( "RainbowBT_users_file", "users.RainbowBT" ); m_strStaticHeaderFile = CFG_GetString( "RainbowBT_static_header", string( ) ); m_strStaticFooterFile = CFG_GetString( "RainbowBT_static_footer", string( ) ); // The Trinity Edition - ADD BEGINS (TRINCUSTSTATDOX) m_strStaticHeaderSignupFile = CFG_GetString( "RainbowBT_static_signup_header", string( ) ); m_strStaticFooterSignupFile = CFG_GetString( "RainbowBT_static_signup_footer", string( ) ); m_strStaticHeaderUploadFile = CFG_GetString( "RainbowBT_static_upload_header", string( ) ); m_strStaticFooterUploadFile = CFG_GetString( "RainbowBT_static_upload_footer", string( ) ); // ---------------------------------------- END OF ADD m_strDumpXMLFile = CFG_GetString( "RainbowBT_dump_xml_file", string( ) ); m_strImageBarFill = CFG_GetString( "image_bar_fill", string( ) ); m_strImageBarTrans = CFG_GetString( "image_bar_trans", string( ) ); m_strForceAnnounceURL = CFG_GetString( "RainbowBT_force_announce_url", string( ) ); m_bForceAnnounceOnDL = CFG_GetInt( "RainbowBT_force_announce_on_download", 0 ) == 0 ? false : true; m_iParseAllowedInterval = CFG_GetInt( "parse_allowed_interval", 0 ); m_iSaveDFileInterval = CFG_GetInt( "save_dfile_interval", 300 ); m_iDownloaderTimeOutInterval = CFG_GetInt( "downloader_timeout_interval", 2700 ); m_iRefreshStaticInterval = CFG_GetInt( "RainbowBT_refresh_static_interval", 10 ); m_iDumpXMLInterval = CFG_GetInt( "RainbowBT_dump_xml_interval", 600 ); m_iParseAllowedNext = GetTime( ) + m_iParseAllowedInterval * 60; m_iSaveDFileNext = GetTime( ) + m_iSaveDFileInterval; m_iPrevTime = 1; m_iDownloaderTimeOutNext = GetTime( ) + m_iDownloaderTimeOutInterval; m_iRefreshStaticNext = 0; m_iDumpXMLNext = GetTime( ) + m_iDumpXMLInterval; m_iAnnounceInterval = CFG_GetInt( "announce_interval", 1800 ); m_iMinRequestInterval = CFG_GetInt( "min_request_interval", 18000 ); m_iResponseSize = CFG_GetInt( "response_size", 50 ); m_iMaxGive = CFG_GetInt( "max_give", 200 ); m_bKeepDead = CFG_GetInt( "keep_dead", 0 ) == 0 ? false : true; m_bAllowScrape = CFG_GetInt( "RainbowBT_allow_scrape", 1 ) == 0 ? false : true; m_bCountUniquePeers = CFG_GetInt( "RainbowBT_count_unique_peers", 1 ) == 0 ? false : true; m_bDeleteInvalid = CFG_GetInt( "RainbowBT_delete_invalid", 0 ) == 0 ? false : true; m_bParseOnUpload = CFG_GetInt( "RainbowBT_parse_on_upload", 1 ) == 0 ? false : true; m_iMaxTorrents = CFG_GetInt( "RainbowBT_max_torrents", 0 ); m_bShowInfoHash = CFG_GetInt( "RainbowBT_show_info_hash", 0 ) == 0 ? false : true; m_bShowNames = CFG_GetInt( "RainbowBT_show_names", 1 ) == 0 ? false : true; m_bShowStats = CFG_GetInt( "RainbowBT_show_stats", 1 ) == 0 ? false : true; m_bAllowTorrentDownloads = CFG_GetInt( "RainbowBT_allow_torrent_downloads", 1 ) == 0 ? false : true; m_bAllowComments = CFG_GetInt( "RainbowBT_allow_comments", 0 ) == 0 ? false : true; m_bShowAdded = CFG_GetInt( "RainbowBT_show_added", 1 ) == 0 ? false : true; m_bShowSize = CFG_GetInt( "RainbowBT_show_size", 1 ) == 0 ? false : true; m_bShowNumFiles = CFG_GetInt( "RainbowBT_show_num_files", 1 ) == 0 ? false : true; m_bShowCompleted = CFG_GetInt( "RainbowBT_show_completed", 0 ) == 0 ? false : true; m_bShowTransferred = CFG_GetInt( "RainbowBT_show_transferred", 0 ) == 0 ? false : true; m_bShowMinLeft = CFG_GetInt( "RainbowBT_show_min_left", 0 ) == 0 ? false : true; m_bShowAverageLeft = CFG_GetInt( "RainbowBT_show_average_left", 0 ) == 0 ? false : true; m_bShowMaxiLeft = CFG_GetInt( "RainbowBT_show_max_left", 0 ) == 0 ? false : true; m_bShowLeftAsProgress = CFG_GetInt( "RainbowBT_show_left_as_progress", 1 ) == 0 ? false : true; m_bShowUploader = CFG_GetInt( "RainbowBT_show_uploader", 0 ) == 0 ? false : true; m_bAllowInfoLink = CFG_GetInt( "RainbowBT_allow_info_link", 0 ) == 0 ? false : true; m_bSearch = CFG_GetInt( "RainbowBT_allow_search", 1 ) == 0 ? false : true; m_bSort = CFG_GetInt( "RainbowBT_allow_sort", 1 ) == 0 ? false : true; m_bShowFileComment = CFG_GetInt( "RainbowBT_show_file_comment", 1 ) == 0 ? false : true; m_bShowFileContents = CFG_GetInt( "RainbowBT_show_file_contents", 0 ) == 0 ? false : true; m_bShowShareRatios = CFG_GetInt( "RainbowBT_show_share_ratios", 1 ) == 0 ? false : true; m_bShowAvgDLRate = CFG_GetInt( "RainbowBT_show_average_dl_rate", 0 ) == 0 ? false : true; m_bShowAvgULRate = CFG_GetInt( "RainbowBT_show_average_ul_rate", 0 ) == 0 ? false : true; m_bDeleteOwnTorrents = CFG_GetInt( "RainbowBT_delete_own_torrents", 1 ) == 0 ? false : true; m_bGen = CFG_GetInt( "RainbowBT_show_gen_time", 1 ) == 0 ? false : true; m_iPerPage = CFG_GetInt( "RainbowBT_per_page", 20 ); m_iUsersPerPage = CFG_GetInt( "RainbowBT_users_per_page", 50 ); m_iMaxPeersDisplay = CFG_GetInt( "RainbowBT_max_peers_display", 500 ); m_iGuestAccess = CFG_GetInt( "RainbowBT_guest_access", ACCESS_VIEW | ACCESS_DL | ACCESS_SIGNUP ); m_iMemberAccess = CFG_GetInt( "RainbowBT_member_access", ACCESS_VIEW | ACCESS_DL | ACCESS_COMMENTS | ACCESS_UPLOAD | ACCESS_SIGNUP ); m_iFileExpires = CFG_GetInt( "RainbowBT_file_expires", 180 ); m_iNameLength = CFG_GetInt( "RainbowBT_name_length", 32 ); m_iCommentLength = CFG_GetInt( "RainbowBT_comment_length", 800 ); // The Trinity Edition - Addition Begins // Retrieves the "trinity_use_mouseovers" value m_bUseMouseovers = CFG_GetInt( "trinity_use_mouseovers", 0 ) == 0 ? false : true; // Retrieves the "RainbowBT_custom_labels" value m_bUseCustomlabels = CFG_GetInt( "RainbowBT_custom_labels", 1 ) == 0 ? false : true; // Retrieves the "RainbowBT_navigation_bar" value m_bUseNavbar = CFG_GetInt( "RainbowBT_navigation_bar", 1 ) == 0 ? false : true; // ------------------------------------------------- End of Addition // tags int iTag = 1; string strName = "RainbowBT_tag" + CAtomInt( iTag ).toString( ); string strTag; while( !( strTag = CFG_GetString( strName, string( ) ) ).empty( ) ) { string :: size_type iSplit = strTag.find( "|" ); strName = "RainbowBT_tag" + CAtomInt( ++iTag ).toString( ); if( iSplit == string :: npos ) m_vecTags.push_back( pair ( strTag, string( ) ) ); else m_vecTags.push_back( pair ( strTag.substr( 0, iSplit ), strTag.substr( iSplit + 1 ) ) ); } m_pAllowed = NULL; m_pTimeDicti = new CAtomDicti( ); m_pCached = new CAtomDicti( ); m_pIPs = new CAtomDicti( ); // decode dfile CAtom *pState = DecodeFile( m_strDFile.c_str( ) ); if( pState && pState->isDicti( ) ) { m_pState = (CAtomDicti *)pState; if( m_pState->getItem( "peers" ) && m_pState->getItem( "completed" ) ) { CAtom *pDFile = m_pState->getItem( "peers" ); if( pDFile && pDFile->isDicti( ) ) m_pDFile = (CAtomDicti *)pDFile; CAtom *pCompleted = m_pState->getItem( "completed" ); if( pCompleted && pCompleted->isDicti( ) ) m_pCompleted = (CAtomDicti *)pCompleted; } else { if( pState ) delete pState; m_pState = new CAtomDicti( ); m_pDFile = new CAtomDicti( ); m_pCompleted = new CAtomDicti( ); m_pState->setItem( "peers", m_pDFile ); m_pState->setItem( "completed", m_pCompleted ); } // populate time dicti map mapDicti = m_pDFile->getValue( ); for( map :: iterator i = mapDicti.begin( ); i != mapDicti.end( ); i++ ) { CAtomDicti *pTS = new CAtomDicti( ); if( (*i).second->isDicti( ) ) { CAtomDicti *pPeersDicti = (CAtomDicti *)(*i).second; map mapPeersDicti = pPeersDicti->getValue( ); for( map :: iterator j = mapPeersDicti.begin( ); j != mapPeersDicti.end( ); j++ ) pTS->setItem( (*j).first, new CAtomLong( 0 ) ); // reset connected times for( map :: iterator j = mapPeersDicti.begin( ); j != mapPeersDicti.end( ); j++ ) { if( (*j).second->isDicti( ) ) { CAtomDicti *pPeerDicti = (CAtomDicti *)(*j).second; if( pPeerDicti ) pPeerDicti->setItem( "connected", new CAtomLong( 0 ) ); } } } m_pTimeDicti->setItem( (*i).first, pTS ); } } else { if( pState ) delete pState; m_pState = new CAtomDicti( ); m_pDFile = new CAtomDicti( ); m_pCompleted = new CAtomDicti( ); m_pState->setItem( "peers", m_pDFile ); m_pState->setItem( "completed", m_pCompleted ); } // decode comments file if( m_strCommentsFile.empty( ) ) m_pComments = new CAtomDicti( ); else { CAtom *pComments = DecodeFile( m_strCommentsFile.c_str( ) ); if( pComments && pComments->isDicti( ) ) m_pComments = (CAtomDicti *)pComments; else { if( pComments ) delete pComments; pComments = NULL; m_pComments = new CAtomDicti( ); } } // decode tag file if( m_strTagFile.empty( ) ) m_pTags = new CAtomDicti( ); else { CAtom *pTags = DecodeFile( m_strTagFile.c_str( ) ); if( pTags && pTags->isDicti( ) ) m_pTags = (CAtomDicti *)pTags; else { if( pTags ) delete pTags; pTags = NULL; m_pTags = new CAtomDicti( ); } } // decode users file if( m_strUsersFile.empty( ) ) m_pUsers = new CAtomDicti( ); else { CAtom *pUsers = DecodeFile( m_strUsersFile.c_str( ) ); if( pUsers && pUsers->isDicti( ) ) m_pUsers = (CAtomDicti *)pUsers; else { if( pUsers ) delete pUsers; pUsers = NULL; m_pUsers = new CAtomDicti( ); } } if( m_bCountUniquePeers ) CountUniquePeers( ); // parse allowed dir if( !m_strAllowedDir.empty( ) ) parseTorrents( m_strAllowedDir.c_str( ) ); // mime gmapMime[".hqx"] = "application/mac-binhex40"; gmapMime[".exe"] = "application/octet-stream"; gmapMime[".pdf"] = "application/pdf"; gmapMime[".gtar"] = "application/x-gtar"; gmapMime[".gz"] = "application/x-gzip"; gmapMime[".js"] = "application/x-javascript"; gmapMime[".swf"] = "application/x-shockwave-flash"; gmapMime[".sit"] = "application/x-stuffit"; gmapMime[".tar"] = "application/x-tar"; gmapMime[".zip"] = "application/zip"; gmapMime[".bmp"] = "image/bmp"; gmapMime[".gif"] = "image/gif"; gmapMime[".jpg"] = "image/jpeg"; gmapMime[".jpeg"] = "image/jpeg"; gmapMime[".jpe"] = "image/jpeg"; gmapMime[".png"] = "image/png"; gmapMime[".tiff"] = "image/tiff"; gmapMime[".tif"] = "image/tiff"; gmapMime[".css"] = "text/css"; gmapMime[".html"] = "text/html"; gmapMime[".htm"] = "text/html"; gmapMime[".txt"] = "text/plain"; gmapMime[".rtf"] = "text/rtf"; gmapMime[".xml"] = "text/xml"; } CTracker :: ~CTracker( ) { delete m_pAllowed; delete m_pState; delete m_pTimeDicti; delete m_pCached; delete m_pComments; delete m_pTags; delete m_pUsers; delete m_pIPs; m_pAllowed = NULL; m_pState = NULL; m_pDFile = NULL; m_pCompleted = NULL; m_pTimeDicti = NULL; m_pCached = NULL; m_pComments = NULL; m_pTags = NULL; m_pUsers = NULL; m_pIPs = NULL; m_mtxQueued.Destroy( ); } void CTracker :: saveDFile( ) { string strData = Encode( m_pState ); FILE *pFile = NULL; if( ( pFile = fopen( m_strDFile.c_str( ), "wb" ) ) == NULL ) { UTIL_LogPrint( "tracker warning - unable to open %s for writing\n", m_strDFile.c_str( ) ); return; } fwrite( (void *)strData.c_str( ), sizeof( char ), strData.size( ), pFile ); fclose( pFile ); } void CTracker :: saveComments( ) { if( m_strCommentsFile.empty( ) ) return; string strData = Encode( m_pComments ); FILE *pFile = NULL; if( ( pFile = fopen( m_strCommentsFile.c_str( ), "wb" ) ) == NULL ) { UTIL_LogPrint( "tracker warning - unable to open %s for writing\n", m_strCommentsFile.c_str( ) ); return; } fwrite( (void *)strData.c_str( ), sizeof( char ), strData.size( ), pFile ); fclose( pFile ); } void CTracker :: saveTags( ) { string strData = Encode( m_pTags ); FILE *pFile = NULL; if( ( pFile = fopen( m_strTagFile.c_str( ), "wb" ) ) == NULL ) { UTIL_LogPrint( "tracker warning - unable to open %s for writing\n", m_strTagFile.c_str( ) ); return; } fwrite( (void *)strData.c_str( ), sizeof( char ), strData.size( ), pFile ); fclose( pFile ); } void CTracker :: saveUsers( ) { string strData = Encode( m_pUsers ); FILE *pFile = NULL; if( ( pFile = fopen( m_strUsersFile.c_str( ), "wb" ) ) == NULL ) { UTIL_LogPrint( "tracker warning - unable to open %s for writing\n", m_strUsersFile.c_str( ) ); return; } fwrite( (void *)strData.c_str( ), sizeof( char ), strData.size( ), pFile ); fclose( pFile ); } void CTracker :: saveXML( ) { string strData; strData += "\n"; strData += " \n"; if( m_pDFile ) { map \n"; FILE *pFile = NULL; if( ( pFile = fopen( m_strDumpXMLFile.c_str( ), "wb" ) ) == NULL ) { UTIL_LogPrint( "tracker warning - unable to open %s for writing\n", m_strDumpXMLFile.c_str( ) ); return; } fwrite( (void *)strData.c_str( ), sizeof( char ), strData.size( ), pFile ); fclose( pFile ); } void CTracker :: expireDownloaders( ) { // use getValuePtr here because we're screwing around with the map structure! if( m_pTimeDicti ) { mapmapDicti = m_pDFile->getValue( ); for( map :: iterator i = mapDicti.begin( ); i != mapDicti.end( ); i++ ) { strData += " getItem( (*i).first ); if( pList && pList->isList( ) ) { vector \n"; } } strData += "vecTorrent = ( (CAtomList *)pList )->getValue( ); if( vecTorrent.size( ) == 6 ) { CAtom *pFileName = vecTorrent[0]; CAtom *pName = vecTorrent[1]; CAtom *pAdded = vecTorrent[2]; CAtom *pSize = vecTorrent[3]; CAtom *pFiles = vecTorrent[4]; if( pFileName ) strData += " filename=\"" + UTIL_RemoveHTML( pFileName->toString( ) ) + "\""; if( pName ) strData += " name=\"" + UTIL_RemoveHTML( pName->toString( ) ) + "\""; if( pAdded ) strData += " added=\"" + pAdded->toString( ) + "\""; if( pSize ) strData += " size=\"" + pSize->toString( ) + "\""; if( pFiles ) strData += " files=\"" + pFiles->toString( ) + "\""; } } } if( m_pCompleted ) { CAtom *pCompleted = m_pCompleted->getItem( (*i).first ); if( pCompleted ) strData += " completed=\"" + pCompleted->toString( ) + "\""; } if( m_pTags ) { CAtom *pDicti = m_pTags->getItem( (*i).first ); if( pDicti && pDicti->isDicti( ) ) { CAtom *pTag = ( (CAtomDicti *)pDicti )->getItem( "tag" ); CAtom *pName = ( (CAtomDicti *)pDicti )->getItem( "name" ); CAtom *pUploader = ( (CAtomDicti *)pDicti )->getItem( "uploader" ); if( pTag ) strData += " tag=\"" + UTIL_RemoveHTML( pTag->toString( ) ) + "\""; if( pName ) strData += " uploadname=\"" + UTIL_RemoveHTML( pName->toString( ) ) + "\""; if( pUploader ) strData += " uploader=\"" + UTIL_RemoveHTML( pUploader->toString( ) ) + "\""; } } strData += ">\n"; if( (*i).second->isDicti( ) ) { map mapPeersDicti = ( (CAtomDicti *)(*i).second )->getValue( ); if( mapPeersDicti.empty( ) ) strData += " \n"; else { strData += " \n"; for( map \n"; } } strData += ":: iterator j = mapPeersDicti.begin( ); j != mapPeersDicti.end( ); j++ ) { if( (*j).second->isDicti( ) ) { CAtomDicti *pPeerDicti = (CAtomDicti *)(*j).second; CAtom *pIP = pPeerDicti->getItem( "ip" ); CAtom *pUpped = pPeerDicti->getItem( "uploaded" ); CAtom *pDowned = pPeerDicti->getItem( "downloaded" ); CAtom *pLef = pPeerDicti->getItem( "left" ); CAtom *pConn = pPeerDicti->getItem( "connected" ); strData += " toString( ) + "\""; if( pUpped ) strData += " uploaded=\"" + pUpped->toString( ) + "\""; if( pDowned ) strData += " downloaded=\"" + pDowned->toString( ) + "\""; if( pLef && pLef->isLong( ) ) strData += " left=\"" + pLef->toString( ) + "\""; if( pConn && pConn->isLong( ) ) strData += " connected=\"" + CAtomLong( GetTime( ) - (unsigned long)( (CAtomLong *)pConn )->getValue( ) ).toString( ) + "\""; strData += " />\n"; } } strData += " mapTimeDicti = m_pTimeDicti->getValue( ); for( map :: iterator i = mapTimeDicti.begin( ); i != mapTimeDicti.end( ); i++ ) { if( (*i).second->isDicti( ) ) { map *pmapPeersDicti = ( (CAtomDicti *)(*i).second )->getValuePtr( ); for( map :: iterator j = pmapPeersDicti->begin( ); j != pmapPeersDicti->end( ); ) { if( (*j).second->isLong( ) && ( (CAtomLong *)(*j).second )->getValue( ) < m_iPrevTime ) { if( m_pDFile ) { CAtom *pPeers = m_pDFile->getItem( (*i).first ); if( pPeers && pPeers->isDicti( ) ) ( (CAtomDicti *)pPeers )->delItem( (*j).first ); } delete (*j).second; pmapPeersDicti->erase( j++ ); } else j++; } } } CountUniquePeers( ); m_iPrevTime = GetTime( ); if( m_bKeepDead ) return; // delete empty hashes if( m_pDFile ) { map *pmapDicti = m_pDFile->getValuePtr( ); for( map :: iterator i = pmapDicti->begin( ); i != pmapDicti->end( ); ) { if( (*i).second->isDicti( ) && ( (CAtomDicti *)(*i).second )->isEmpty( ) ) { m_pTimeDicti->delItem( (*i).first ); delete (*i).second; pmapDicti->erase( i++ ); } else i++; } } } } void CTracker :: parseTorrents( const char *szDir ) { CAtomDicti *pAllowed = new CAtomDicti( ); #ifdef WIN32 char szMatch[1024]; memset( szMatch, 0, sizeof( char ) * 1024 ); strcpy( szMatch, szDir ); strcat( szMatch, "*.torrent" ); WIN32_FIND_DATA fdt; HANDLE hFind = FindFirstFile( szMatch, &fdt ); if( hFind != INVALID_HANDLE_VALUE ) { do #else DIR *pDir = opendir( szDir ); struct dirent *dp; if( pDir ) { while( ( dp = readdir( pDir ) ) ) #endif { // let the server accept new connections while parsing if( gpServer ) gpServer->Update( false ); char szFile[1024]; memset( szFile, 0, sizeof( char ) * 1024 ); strcpy( szFile, szDir ); #ifdef WIN32 string strFileName = fdt.cFileName; #else string strFileName = dp->d_name; #endif strcat( szFile, strFileName.c_str( ) ); #ifndef WIN32 if( strlen( szFile ) > strlen( ".torrent" ) ) { if( strcmp( szFile + strlen( szFile ) - strlen( ".torrent" ), ".torrent" ) ) continue; } else continue; #endif CAtom *pFile = DecodeFile( szFile ); if( pFile && pFile->isDicti( ) ) { CAtom *pInfo = ( (CAtomDicti *)pFile )->getItem( "info" ); if( pInfo && pInfo->isDicti( ) ) { CAtomDicti *pInfoDicti = (CAtomDicti *)pInfo; string strHash = UTIL_InfoHash( pFile ); CAtom *pName = pInfoDicti->getItem( "name" ); CAtom *pLen = pInfoDicti->getItem( "length" ); CAtom *pFiles = pInfoDicti->getItem( "files" ); if( pName && ( pLen && pLen->isLong( ) || ( pFiles && pFiles->isList( ) ) ) ) { CAtomList *pList = new CAtomList( ); // // filename // pList->addItem( new CAtomString( strFileName ) ); // // name // pList->addItem( new CAtomString( pName->toString( ) ) ); // // added time (i.e. modification time) // char pTime[256]; memset( pTime, 0, sizeof( char ) * 256 ); #ifdef WIN32 FILETIME ft; SYSTEMTIME st; FileTimeToLocalFileTime( &fdt.ftLastWriteTime, &ft ); FileTimeToSystemTime( &ft, &st ); sprintf( pTime, "%04d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond ); #else struct stat info; stat( szFile, &info ); strftime( pTime, sizeof( char ) * 256, "%Y-%m-%d %H:%M:%S", localtime( &info.st_mtime ) ); #endif pList->addItem( new CAtomString( pTime ) ); // // file size // if( pLen ) pList->addItem( new CAtomLong( *(CAtomLong *)pLen ) ); else { int64 iSize = 0; vector vecFiles = ( (CAtomList *)pFiles )->getValue( ); for( vector :: iterator j = vecFiles.begin( ); j != vecFiles.end( ); j++ ) { if( (*j)->isDicti( ) ) { CAtom *pSubLength = ( (CAtomDicti *)(*j) )->getItem( "length" ); if( pSubLength && pSubLength->isLong( ) ) iSize += ( (CAtomLong *)pSubLength )->getValue( ); } } pList->addItem( new CAtomLong( iSize ) ); } // // number of files // if( pLen ) pList->addItem( new CAtomInt( 1 ) ); else pList->addItem( new CAtomInt( ( (CAtomList *)pFiles )->getValue( ).size( ) ) ); // // file comment // CAtom *pComment = ( (CAtomDicti *)pFile )->getItem( "comment" ); if( pComment ) pList->addItem( new CAtomString( pComment->toString( ) ) ); else pList->addItem( new CAtomString( ) ); pAllowed->setItem( strHash, pList ); if( m_pDFile && m_bKeepDead ) { if( !m_pDFile->getItem( strHash ) ) m_pDFile->setItem( strHash, new CAtomDicti( ) ); } } else { UTIL_LogPrint( "error parsing torrents - %s has an incomplete or invalid info key, skipping\n", strFileName.c_str( ) ); if( m_bDeleteInvalid ) { if( m_strArchiveDir.empty( ) ) UTIL_DeleteFile( szFile ); else UTIL_MoveFile( szFile, ( m_strArchiveDir + strFileName ).c_str( ) ); } } } else { UTIL_LogPrint( "error parsing torrents - %s doesn't have an info key or info is not a valid bencoded dictionary, skipping\n", strFileName.c_str( ) ); if( m_bDeleteInvalid ) { if( m_strArchiveDir.empty( ) ) UTIL_DeleteFile( szFile ); else UTIL_MoveFile( szFile, ( m_strArchiveDir + strFileName ).c_str( ) ); } } } else { UTIL_LogPrint( "error parsing torrents - %s is not a valid bencoded dictionary or unable to decode, skipping\n", strFileName.c_str( ) ); if( m_bDeleteInvalid ) { if( m_strArchiveDir.empty( ) ) UTIL_DeleteFile( szFile ); else UTIL_MoveFile( szFile, ( m_strArchiveDir + strFileName ).c_str( ) ); } } delete pFile; pFile = NULL; #ifdef WIN32 } while( FindNextFile( hFind, &fdt ) ); FindClose( hFind ); #else } closedir( pDir ); #endif } else UTIL_LogPrint( "error parsing torrents - unable to open %s or no torrents found\n", szDir ); if( m_pAllowed ) delete m_pAllowed; m_pAllowed = pAllowed; } void CTracker :: parseTorrent( const char *szFile ) { #ifdef WIN32 HANDLE hFile = CreateFile( szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if( hFile == INVALID_HANDLE_VALUE ) { UTIL_LogPrint( "error parsing torrent - unable to open %s for reading\n", szFile ); return; } #endif CAtom *pTorrent = DecodeFile( szFile ); if( pTorrent && pTorrent->isDicti( ) ) { CAtom *pInfo = ( (CAtomDicti *)pTorrent )->getItem( "info" ); if( pInfo && pInfo->isDicti( ) ) { CAtomDicti *pInfoDicti = (CAtomDicti *)pInfo; string strHash = UTIL_InfoHash( pTorrent ); CAtom *pName = pInfoDicti->getItem( "name" ); CAtom *pLen = pInfoDicti->getItem( "length" ); CAtom *pFiles = pInfoDicti->getItem( "files" ); if( pName && ( pLen && pLen->isLong( ) || ( pFiles && pFiles->isList( ) ) ) ) { CAtomList *pList = new CAtomList( ); // // filename // pList->addItem( new CAtomString( UTIL_StripPath( szFile ).c_str( ) ) ); // // name // pList->addItem( new CAtomString( pName->toString( ) ) ); // // added time (i.e. modification time) // char pTime[256]; memset( pTime, 0, sizeof( char ) * 256 ); #ifdef WIN32 FILETIME ft; SYSTEMTIME st; GetFileTime( hFile, NULL, NULL, &ft ); FileTimeToLocalFileTime( &ft, &ft ); FileTimeToSystemTime( &ft, &st ); sprintf( pTime, "%04d-%02d-%02d %02d:%02d:%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond ); #else struct stat info; stat( szFile, &info ); strftime( pTime, sizeof( char ) * 256, "%Y-%m-%d %H:%M:%S", localtime( &info.st_mtime ) ); #endif pList->addItem( new CAtomString( pTime ) ); // // file size // if( pLen ) pList->addItem( new CAtomLong( *(CAtomLong *)pLen ) ); else { int64 iSize = 0; vector vecFiles = ( (CAtomList *)pFiles )->getValue( ); for( vector :: iterator j = vecFiles.begin( ); j != vecFiles.end( ); j++ ) { if( (*j)->isDicti( ) ) { CAtom *pSubLength = ( (CAtomDicti *)(*j) )->getItem( "length" ); if( pSubLength && pSubLength->isLong( ) ) iSize += ( (CAtomLong *)pSubLength )->getValue( ); } } pList->addItem( new CAtomLong( iSize ) ); } // // number of files // if( pLen ) pList->addItem( new CAtomInt( 1 ) ); else pList->addItem( new CAtomInt( ( (CAtomList *)pFiles )->getValue( ).size( ) ) ); // // file comment // CAtom *pComment = ( (CAtomDicti *)pTorrent )->getItem( "comment" ); if( pComment ) pList->addItem( new CAtomString( pComment->toString( ) ) ); else pList->addItem( new CAtomString( ) ); m_pAllowed->setItem( strHash, pList ); if( m_pDFile && m_bKeepDead ) { if( !m_pDFile->getItem( strHash ) ) m_pDFile->setItem( strHash, new CAtomDicti( ) ); } } else { UTIL_LogPrint( "error parsing torrent - %s has an incomplete or invalid info key, skipping\n", szFile ); if( m_bDeleteInvalid ) { if( m_strArchiveDir.empty( ) ) UTIL_DeleteFile( szFile ); else UTIL_MoveFile( szFile, ( m_strArchiveDir + UTIL_StripPath( szFile ) ).c_str( ) ); } } } else { UTIL_LogPrint( "error parsing torrent - %s doesn't have an info key or info is not a valid bencoded dictionary, skipping\n", szFile ); if( m_bDeleteInvalid ) { if( m_strArchiveDir.empty( ) ) UTIL_DeleteFile( szFile ); else UTIL_MoveFile( szFile, ( m_strArchiveDir + UTIL_StripPath( szFile ) ).c_str( ) ); } } } else { UTIL_LogPrint( "error parsing torrent - %s is not a valid bencoded dictionary or unable to decode, skipping\n", szFile ); if( m_bDeleteInvalid ) { if( m_strArchiveDir.empty( ) ) UTIL_DeleteFile( szFile ); else UTIL_MoveFile( szFile, ( m_strArchiveDir + UTIL_StripPath( szFile ) ).c_str( ) ); } } delete pTorrent; pTorrent = NULL; #ifdef WIN32 CloseHandle( hFile ); #endif } bool CTracker :: checkTag( string &strTag ) { if( m_vecTags.size( ) == 0 ) return true; for( vector< pair > :: iterator i = m_vecTags.begin( ); i != m_vecTags.end( ); i++ ) { if( (*i).first == strTag ) return true; } return false; } void CTracker :: addTag( string strInfoHash, string strTag, string strName, string strUploader, string strInfoLink ) { if( !m_pTags->getItem( strInfoHash ) ) m_pTags->setItem( strInfoHash, new CAtomDicti( ) ); CAtom *pTag = m_pTags->getItem( strInfoHash ); if( pTag && pTag->isDicti( ) ) { ( (CAtomDicti *)pTag )->setItem( "tag", new CAtomString( strTag ) ); if( !strName.empty( ) ) ( (CAtomDicti *)pTag )->setItem( "name", new CAtomString( strName ) ); if( !strUploader.empty( ) ) ( (CAtomDicti *)pTag )->setItem( "uploader", new CAtomString( strUploader ) ); // The Trinity Edition - Addition Begins // The following code allows the removal of the Uploader's name for any // given torrent by setting this value to REMOVE and saving changes // This code is brought to you by courtesy of user ConfusedFish :) if( ( !strUploader.empty( ) ) && ( strUploader!="REMOVE" ) ) ( (CAtomDicti *)pTag )->setItem( "uploader", new CAtomString( strUploader ) ); if( strUploader == "REMOVE" ) ( (CAtomDicti *)pTag )->delItem( "uploader" ); // ------------------------------------------------- End of Addition if( !strInfoLink.empty( ) ) ( (CAtomDicti *)pTag )->setItem( "infolink", new CAtomString( strInfoLink ) ); // The Trinity Edition - Addition Begins // The following code allows the removal of the InfoLink for any // given torrent by setting this value to REMOVE and saving changes // This code is brought to you by courtesy of user ConfusedFish :) if( ( !strInfoLink.empty( ) ) && ( strInfoLink!="REMOVE" ) ) ( (CAtomDicti *)pTag )->setItem( "infolink", new CAtomString( strInfoLink ) ); if( strInfoLink == "REMOVE" ) ( (CAtomDicti *)pTag )->delItem( "infolink" ); // ------------------------------------------------- End of Addition } saveTags( ); } void CTracker :: deleteTag( string strInfoHash ) { m_pTags->delItem( strInfoHash ); saveTags( ); } user_t CTracker :: checkUser( string strLogin, string strMD5 ) { user_t user; user.iAccess = m_iGuestAccess; // if no users exist, grant full access if( m_pUsers->isEmpty( ) ) user.iAccess = ~0; CAtom *pUser = m_pUsers->getItem( strLogin ); if( pUser && pUser->isDicti( ) ) { CAtom *pMD5 = ( (CAtomDicti *)pUser )->getItem( "md5" ); CAtom *pAccess = ( (CAtomDicti *)pUser )->getItem( "access" ); CAtom *pMail = ( (CAtomDicti *)pUser )->getItem( "email" ); CAtom *pCreated = ( (CAtomDicti *)pUser )->getItem( "created" ); if( pMD5 && pAccess && pAccess->isLong( ) && pMail ) { // check hash if( strMD5 == pMD5->toString( ) ) { user.strLogin = strLogin; user.strLowerLogin = UTIL_ToLower( user.strLogin ); user.strMD5 = strMD5; user.strMail = pMail->toString( ); user.strLowerMail = UTIL_ToLower( user.strMail ); user.iAccess = (int)( (CAtomLong *)pAccess )->getValue( ); user.strCreated = pCreated->toString( ); } } } return user; } void CTracker :: addUser( string strLogin, string strPass, int iAccess, string strMail ) { CAtomDicti *pUser = new CAtomDicti( ); // calculate md5 hash of A1 string strA1 = strLogin + ":" + gstrRealm + ":" + strPass; unsigned char szMD5[16]; MD5_CTX md5; MD5Init( &md5 ); MD5Update( &md5, (unsigned char *)strA1.c_str( ), strA1.size( ) ); MD5Final( szMD5, &md5 ); pUser->setItem( "md5", new CAtomString( string( (char *)szMD5, 16 ) ) ); pUser->setItem( "access", new CAtomLong( iAccess ) ); pUser->setItem( "email", new CAtomString( strMail ) ); time_t tNow = time( NULL ); char pTime[256]; memset( pTime, 0, sizeof( char ) * 256 ); strftime( pTime, sizeof( char ) * 256, "%Y %m/%d %H:%M:%S", localtime( &tNow ) ); pUser->setItem( "created", new CAtomString( pTime ) ); m_pUsers->setItem( strLogin, pUser ); saveUsers( ); } void CTracker :: deleteUser( string strLogin ) { m_pUsers->delItem( strLogin ); saveUsers( ); } void CTracker :: CountUniquePeers( ) { delete m_pIPs; m_pIPs = new CAtomDicti( ); map mapDicti = m_pDFile->getValue( ); for( map :: iterator i = mapDicti.begin( ); i != mapDicti.end( ); i++ ) { if( (*i).second->isDicti( ) ) { map mapPeersDicti = ( (CAtomDicti *)(*i).second )->getValue( ); for( map :: iterator j = mapPeersDicti.begin( ); j != mapPeersDicti.end( ); j++ ) { if( (*j).second->isDicti( ) ) { CAtom *pIP = ( (CAtomDicti *)(*j).second )->getItem( "ip" ); if( pIP ) AddUniquePeer( pIP->toString( ) ); } } } } } void CTracker :: AddUniquePeer( string strIP ) { // increment unique count for this ip CAtom *pNum = m_pIPs->getItem( strIP ); int iNum = 1; if( pNum && pNum->isInt( ) ) iNum = ( (CAtomInt *)pNum )->getValue( ) + 1; m_pIPs->setItem( strIP, new CAtomInt( iNum ) ); } void CTracker :: RemoveUniquePeer( string strIP ) { // decrement unique count for this ip CAtom *pNum = m_pIPs->getItem( strIP ); int iNum = 0; if( pNum && pNum->isInt( ) ) iNum = ( (CAtomInt *)pNum )->getValue( ) - 1; if( iNum > 0 ) m_pIPs->setItem( strIP, new CAtomInt( iNum ) ); else m_pIPs->delItem( strIP ); } void CTracker :: QueueAnnounce( CAtomDicti *pParams ) { // normally called from link.cpp m_mtxQueued.Claim( ); m_vecQueued.push_back( new CAtomDicti( *pParams ) ); m_mtxQueued.Release( ); } void CTracker :: serverResponseGET( struct request_t *pRequest, struct response_t *pResponse, user_t user ) { if( pRequest->strURL == "/" || pRequest->strURL == "/index.html" ) serverResponseIndex( pRequest, pResponse, user ); else if( pRequest->strURL == "/announce" ) serverResponseAnnounce( pRequest, pResponse, user ); else if( pRequest->strURL == "/scrape" ) serverResponseScrape( pRequest, pResponse, user ); else if( pRequest->strURL == "/stats.html" ) serverResponseStats( pRequest, pResponse, user ); else if( pRequest->strURL == "/comments.html" ) serverResponseComments( pRequest, pResponse, user ); else if( pRequest->strURL == "/admin.html" ) serverResponseAdmin( pRequest, pResponse, user ); else if( pRequest->strURL.substr( 0, 7 ) == "/files/" ) serverResponseFile( pRequest, pResponse, user ); else if( pRequest->strURL == "/torrent.html" ) serverResponseTorrent( pRequest, pResponse, user ); else if( pRequest->strURL == "/upload.html" ) serverResponseUploadGET( pRequest, pResponse, user ); else if( pRequest->strURL == "/users.html" ) serverResponseUsers( pRequest, pResponse, user ); else if( pRequest->strURL == "/login.html" ) serverResponseLogin( pRequest, pResponse, user ); else if( pRequest->strURL == "/info.html" ) serverResponseInfo( pRequest, pResponse, user ); else if( pRequest->strURL == "/signup.html" ) serverResponseSignup( pRequest, pResponse, user ); else pResponse->strCode = "404 Not Found"; } void CTracker :: serverResponsePOST( struct request_t *pRequest, struct response_t *pResponse, CAtomList *pPost, user_t user ) { if( pPost ) { if( pRequest->strURL == "/upload.html" ) serverResponseUploadPOST( pRequest, pResponse, pPost, user ); else pResponse->strCode = "404 Not Found"; } else pResponse->strCode = "500 Server Error"; } void CTracker :: Update( ) { if( !m_strAllowedDir.empty( ) && m_iParseAllowedInterval > 0 ) { if( GetTime( ) > m_iParseAllowedNext ) { if( gbDebug ) UTIL_LogPrint( "tracker - parsing torrents (%s)\n", m_strAllowedDir.c_str( ) ); // don't parse torrents again until we're done since parseTorrents calls gpServer->Update( ) which calls m_pTracker->Update( ) m_iParseAllowedNext = GetTime( ) + 9999; parseTorrents( m_strAllowedDir.c_str( ) ); m_iParseAllowedNext = GetTime( ) + m_iParseAllowedInterval * 60; } } if( GetTime( ) > m_iSaveDFileNext ) { if( gbDebug ) UTIL_LogPrint( "tracker - saving dfile (%s)\n", m_strDFile.c_str( ) ); saveDFile( ); m_iSaveDFileNext = GetTime( ) + m_iSaveDFileInterval; } // The Trinity Edition - Modification Begins (TRINCUSTSTATDOX) // Adds the SIGNUP AND UPLOAD Static Header and Footer to be handled by the Static Refresh Value if( !m_strStaticHeaderFile.empty( ) || !m_strStaticFooterFile.empty( ) || !m_strStaticHeaderSignupFile.empty( ) || !m_strStaticFooterSignupFile.empty( ) || !m_strStaticHeaderUploadFile.empty( ) || !m_strStaticFooterUploadFile.empty( )) { if( GetTime( ) > m_iRefreshStaticNext ) { if( gbDebug ) UTIL_LogPrint( "tracker - refreshing static header and footer\n" ); if( !m_strStaticHeaderFile.empty( ) ) m_strStaticHeader = UTIL_ReadFile( m_strStaticHeaderFile.c_str( ) ); if( !m_strStaticFooterFile.empty( ) ) m_strStaticFooter = UTIL_ReadFile( m_strStaticFooterFile.c_str( ) ); if( !m_strStaticHeaderSignupFile.empty( ) ) m_strStaticHeaderSignup = UTIL_ReadFile( m_strStaticHeaderSignupFile.c_str( ) ); if( !m_strStaticFooterSignupFile.empty( ) ) m_strStaticFooterSignup = UTIL_ReadFile( m_strStaticFooterSignupFile.c_str( ) ); if( !m_strStaticHeaderUploadFile.empty( ) ) m_strStaticHeaderUpload = UTIL_ReadFile( m_strStaticHeaderUploadFile.c_str( ) ); if( !m_strStaticFooterUploadFile.empty( ) ) m_strStaticFooterUpload = UTIL_ReadFile( m_strStaticFooterUploadFile.c_str( ) ); m_iRefreshStaticNext = GetTime( ) + m_iRefreshStaticInterval * 60; } } /* Original Source Code: if( !m_strStaticHeaderFile.empty( ) || !m_strStaticFooterFile.empty( ) ) { if( GetTime( ) > m_iRefreshStaticNext ) { if( gbDebug ) UTIL_LogPrint( "tracker - refreshing static header and footer\n" ); if( !m_strStaticHeaderFile.empty( ) ) m_strStaticHeader = UTIL_ReadFile( m_strStaticHeaderFile.c_str( ) ); if( !m_strStaticFooterFile.empty( ) ) m_strStaticFooter = UTIL_ReadFile( m_strStaticFooterFile.c_str( ) ); m_iRefreshStaticNext = GetTime( ) + m_iRefreshStaticInterval * 60; } } */ if( GetTime( ) > m_iDownloaderTimeOutNext ) { if( gbDebug ) UTIL_LogPrint( "tracker - expiring downloaders\n" ); expireDownloaders( ); m_iDownloaderTimeOutNext = GetTime( ) + m_iDownloaderTimeOutInterval; } if( !m_strDumpXMLFile.empty( ) ) { if( GetTime( ) > m_iDumpXMLNext ) { if( gbDebug ) UTIL_LogPrint( "tracker - dumping xml\n" ); saveXML( ); m_iDumpXMLNext = GetTime( ) + m_iDumpXMLInterval; } } // queued announces m_mtxQueued.Claim( ); vector vecTemp = m_vecQueued; m_vecQueued.clear( ); m_mtxQueued.Release( ); for( vector :: iterator i = vecTemp.begin( ); i != vecTemp.end( ); i++ ) { CAtom *pInfoHash = (*i)->getItem( "info_hash" ); CAtom *pIP = (*i)->getItem( "ip" ); CAtom *pEvent = (*i)->getItem( "event" ); CAtom *pPort = (*i)->getItem( "port" ); CAtom *pUploaded = (*i)->getItem( "uploaded" ); CAtom *pDownloaded = (*i)->getItem( "downloaded" ); CAtom *pLeft = (*i)->getItem( "left" ); CAtom *pPeerID = (*i)->getItem( "peer_id" ); string strInfoHash; string strIP; string strEvent; int iPort; int64 iUploaded; int64 iDownloaded; int64 iLeft; string strPeerID; if( pInfoHash && pIP && pPort && pUploaded && pDownloaded && pLeft && pPeerID ) { strInfoHash = pInfoHash->toString( ); strIP = pIP->toString( ); iPort = (int)( (CAtomLong *)pPort )->getValue( ); iUploaded = ( (CAtomLong *)pUploaded )->getValue( ); iDownloaded = ( (CAtomLong *)pDownloaded )->getValue( ); iLeft = ( (CAtomLong *)pLeft )->getValue( ); strPeerID = pPeerID->toString( ); } if( pEvent ) strEvent = pEvent->toString( ); // // begin announce // if( m_pAllowed ) { if( !m_pAllowed->getItem( strInfoHash ) ) continue; } if( strEvent != "stopped" ) { if( m_pDFile ) { if( !m_pDFile->getItem( strInfoHash ) ) m_pDFile->setItem( strInfoHash, new CAtomDicti( ) ); CAtom *pPeers = m_pDFile->getItem( strInfoHash ); if( pPeers && pPeers->isDicti( ) ) { CAtom *pPeer = ( (CAtomDicti *)pPeers )->getItem( strPeerID ); if( pPeer && pPeer->isDicti( ) ) { ( (CAtomDicti *)pPeer )->setItem( "uploaded", new CAtomLong( iUploaded ) ); ( (CAtomDicti *)pPeer )->setItem( "downloaded", new CAtomLong( iDownloaded ) ); ( (CAtomDicti *)pPeer )->setItem( "left", new CAtomLong( iLeft ) ); } else { CAtomDicti *pPeerDicti = new CAtomDicti( ); pPeerDicti->setItem( "ip", new CAtomString( strIP ) ); pPeerDicti->setItem( "port", new CAtomLong( iPort ) ); pPeerDicti->setItem( "uploaded", new CAtomLong( iUploaded ) ); pPeerDicti->setItem( "downloaded", new CAtomLong( iDownloaded ) ); pPeerDicti->setItem( "left", new CAtomLong( iLeft ) ); pPeerDicti->setItem( "connected", new CAtomLong( GetTime( ) ) ); ( (CAtomDicti *)pPeers )->setItem( strPeerID, pPeerDicti ); if( m_bCountUniquePeers ) AddUniquePeer( strIP ); } if( m_pTimeDicti ) { if( !m_pTimeDicti->getItem( strInfoHash ) ) m_pTimeDicti->setItem( strInfoHash, new CAtomDicti( ) ); CAtom *pTS = m_pTimeDicti->getItem( strInfoHash ); if( pTS && pTS->isDicti( ) ) ( (CAtomDicti *)pTS )->setItem( strPeerID, new CAtomLong( GetTime( ) ) ); } } } if( strEvent == "completed" ) { if( m_pCompleted ) { CAtom *pCompleted = m_pCompleted->getItem( strInfoHash ); int64 iCompleted = 0; if( pCompleted && pCompleted->isLong( ) ) iCompleted = ( (CAtomLong *)pCompleted )->getValue( ); m_pCompleted->setItem( strInfoHash, new CAtomLong( iCompleted + 1 ) ); } } } else { // strEvent == "stopped" if( m_pDFile ) { if( !m_pDFile->getItem( strInfoHash ) ) m_pDFile->setItem( strInfoHash, new CAtomDicti( ) ); CAtom *pPeers = m_pDFile->getItem( strInfoHash ); if( pPeers && pPeers->isDicti( ) ) { CAtom *pPeer = ( (CAtomDicti *)pPeers )->getItem( strPeerID ); if( pPeer && pPeer->isDicti( ) ) { CAtom *pPeerIP = ( (CAtomDicti *)pPeer )->getItem( "ip" ); if( pPeerIP ) { if( pPeerIP->toString( ) == strIP ) { ( (CAtomDicti *)pPeers )->delItem( strPeerID ); if( m_pTimeDicti ) { if( !m_pTimeDicti->getItem( strInfoHash ) ) m_pTimeDicti->setItem( strInfoHash, new CAtomDicti( ) ); CAtom *pTS = m_pTimeDicti->getItem( strInfoHash ); if( pTS && pTS->isDicti( ) ) ( (CAtomDicti *)pTS )->delItem( strPeerID ); } if( m_bCountUniquePeers ) RemoveUniquePeer( strIP ); } } } } } } // // end announce // delete *i; } }