PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/core/FileTransfer.cpp

https://gitlab.com/cnchyan/beebeep
C++ | 377 lines | 308 code | 46 blank | 23 comment | 49 complexity | ab3a3c3455d98e075153067a2fcd00ad MD5 | raw file
  1. //////////////////////////////////////////////////////////////////////
  2. //
  3. // This file is part of BeeBEEP.
  4. //
  5. // BeeBEEP is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published
  7. // by the Free Software Foundation, either version 3 of the License,
  8. // or (at your option) any later version.
  9. //
  10. // BeeBEEP is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with BeeBEEP. If not, see <http://www.gnu.org/licenses/>.
  17. //
  18. // Author: Marco Mastroddi <marco.mastroddi(AT)gmail.com>
  19. //
  20. // $Id$
  21. //
  22. //////////////////////////////////////////////////////////////////////
  23. #include "FileShare.h"
  24. #include "FileTransfer.h"
  25. #include "FileTransferPeer.h"
  26. #include "Random.h"
  27. #include "Settings.h"
  28. #include "Protocol.h"
  29. FileTransfer::FileTransfer( QObject *parent )
  30. : QTcpServer( parent ), m_files(), m_peers()
  31. {
  32. }
  33. bool FileTransfer::startListener()
  34. {
  35. qDebug() << "Starting File Transfer server";
  36. if( !listen( Settings::instance().hostAddressToListen(), Settings::instance().defaultFileTransferPort() ) )
  37. {
  38. qWarning() << "Unable to bind default file transfer port" << Settings::instance().defaultFileTransferPort();
  39. if( !listen( Settings::instance().hostAddressToListen() ) )
  40. {
  41. qWarning() << "Unable to bind a valid file transfer port";
  42. return false;
  43. }
  44. }
  45. qDebug() << "File Transfer server listen" << serverAddress().toString() << serverPort();
  46. resetServerFiles();
  47. emit listening();
  48. if( downloadsInQueue() > 0 )
  49. qDebug() << "File Transfer has" << downloadsInQueue() << "files in queue";
  50. return true;
  51. }
  52. void FileTransfer::stopListener()
  53. {
  54. if( isListening() )
  55. {
  56. foreach( FileTransferPeer* transfer_peer, m_peers )
  57. transfer_peer->cancelTransfer();
  58. m_files.clear();
  59. qDebug() << "File Transfer server closed";
  60. close();
  61. }
  62. else
  63. qDebug() << "File Transfer server is not active";
  64. }
  65. void FileTransfer::resetServerFiles()
  66. {
  67. qDebug() << "File Transfer reset files to" << Settings::instance().localUser().hostAddress().toString() << serverPort();
  68. QList<FileInfo>::iterator it = m_files.begin();
  69. while( it != m_files.end() )
  70. {
  71. (*it).setHostAddress( Settings::instance().localUser().hostAddress() );
  72. (*it).setHostPort( serverPort() );
  73. ++it;
  74. }
  75. }
  76. FileInfo FileTransfer::fileInfo( VNumber file_id ) const
  77. {
  78. QList<FileInfo>::const_iterator it = m_files.begin();
  79. while( it != m_files.end() )
  80. {
  81. if( (*it).id() == file_id )
  82. return *it;
  83. ++it;
  84. }
  85. return FileInfo();
  86. }
  87. FileInfo FileTransfer::fileInfoFromPath( const QString& file_absolute_path ) const
  88. {
  89. QList<FileInfo>::const_iterator it = m_files.begin();
  90. while( it != m_files.end() )
  91. {
  92. if( (*it).path() == file_absolute_path )
  93. return *it;
  94. ++it;
  95. }
  96. return FileInfo();
  97. }
  98. void FileTransfer::removeFile( const QFileInfo& fi )
  99. {
  100. QString file_path = fi.absoluteFilePath();
  101. QList<FileInfo>::iterator it = m_files.begin();
  102. while( it != m_files.end() )
  103. {
  104. if( (*it).path() == file_path )
  105. it = m_files.erase( it );
  106. else
  107. ++it;
  108. }
  109. }
  110. FileInfo FileTransfer::addFile( const QFileInfo& fi, const QString& share_folder )
  111. {
  112. FileInfo file_info = fileInfoFromPath( fi.absoluteFilePath() );
  113. if( file_info.isValid() )
  114. {
  115. QString file_hash = Protocol::instance().fileInfoHash( fi );
  116. if( file_info.fileHash() == file_hash )
  117. return file_info;
  118. else
  119. removeFile( fi );
  120. }
  121. file_info = Protocol::instance().fileInfo( fi, share_folder );
  122. file_info.setHostAddress( Settings::instance().localUser().hostAddress() );
  123. file_info.setHostPort( serverPort() );
  124. m_files.append( file_info );
  125. return file_info;
  126. }
  127. void FileTransfer::addFileInfoList( const QList<FileInfo>& file_info_list )
  128. {
  129. foreach( FileInfo fi, file_info_list )
  130. {
  131. if( !fileInfo( fi.id() ).isValid() )
  132. m_files.append( fi );
  133. }
  134. }
  135. void FileTransfer::incomingConnection( qintptr socket_descriptor )
  136. {
  137. FileTransferPeer *upload_peer = new FileTransferPeer( this );
  138. upload_peer->setTransferType( FileTransferPeer::Upload );
  139. upload_peer->setId( Protocol::instance().newId() );
  140. m_peers.append( upload_peer );
  141. setupPeer( upload_peer, socket_descriptor );
  142. }
  143. void FileTransfer::setupPeer( FileTransferPeer* transfer_peer, int socket_descriptor )
  144. {
  145. if( transfer_peer->isInQueue() )
  146. {
  147. #ifdef BEEBEEP_DEBUG
  148. qDebug() << transfer_peer->name() << "is removed from queue";
  149. #endif
  150. transfer_peer->removeFromQueue();
  151. }
  152. #ifdef BEEBEEP_DEBUG
  153. qDebug() << transfer_peer->name() << "starts its connection. Active downloads:" << activeDownloads();
  154. #endif
  155. if( !transfer_peer->isDownload() )
  156. {
  157. connect( transfer_peer, SIGNAL( fileUploadRequest( VNumber, const QByteArray& ) ), this, SLOT( checkUploadRequest( VNumber, const QByteArray& ) ) );
  158. }
  159. connect( transfer_peer, SIGNAL( authenticationRequested() ), this, SLOT( checkAuthentication() ) );
  160. connect( transfer_peer, SIGNAL( progress( VNumber, VNumber, const FileInfo&, FileSizeType ) ), this, SIGNAL( progress( VNumber, VNumber, const FileInfo&, FileSizeType ) ) );
  161. connect( transfer_peer, SIGNAL( message( VNumber, VNumber, const FileInfo&, const QString& ) ), this, SIGNAL( message( VNumber, VNumber, const FileInfo&, const QString& ) ) );
  162. connect( transfer_peer, SIGNAL( destroyed() ), this, SLOT( peerDestroyed() ) );
  163. transfer_peer->setConnectionDescriptor( socket_descriptor );
  164. int delay = Random::number( 1, 9 ) * 100;
  165. #ifdef BEEBEEP_DEBUG
  166. qDebug() << transfer_peer->name() << "starts in" << delay << "ms";
  167. #endif
  168. QTimer::singleShot( delay, transfer_peer, SLOT( startConnection() ) );
  169. }
  170. void FileTransfer::checkAuthentication()
  171. {
  172. FileTransferPeer* mp_peer = (FileTransferPeer*)sender();
  173. if( !mp_peer )
  174. {
  175. qWarning() << "Sender Peer not found in check authentication";
  176. return;
  177. }
  178. qDebug() << mp_peer->name() << "checks authentication message";
  179. emit userConnected( mp_peer->id(), mp_peer->peerAddress(), mp_peer->messageAuth() );
  180. }
  181. void FileTransfer::validateUser( VNumber FileTransferPeer_id, VNumber user_id )
  182. {
  183. qDebug() << "File Transfer server validate user" << user_id << "for peer" << FileTransferPeer_id;
  184. QList<FileTransferPeer*>::iterator it = m_peers.begin();
  185. while( it != m_peers.end() )
  186. {
  187. if( (*it)->id() == FileTransferPeer_id )
  188. {
  189. if( user_id == ID_INVALID )
  190. {
  191. qWarning() << (*it)->name() << "has not authorized the user";
  192. (*it)->cancelTransfer();
  193. }
  194. else
  195. {
  196. qDebug() << (*it)->name() << "has authorized user" << user_id;
  197. (*it)->setUserAuthorized( user_id );
  198. }
  199. return;
  200. }
  201. ++it;
  202. }
  203. }
  204. void FileTransfer::checkUploadRequest( VNumber file_id, const QByteArray& file_password )
  205. {
  206. #ifdef BEEBEEP_DEBUG
  207. qDebug() << "Checking upload request:" << file_id << file_password;
  208. #endif
  209. FileTransferPeer *upload_peer = qobject_cast<FileTransferPeer*>( sender() );
  210. if( !upload_peer )
  211. {
  212. qWarning() << "File Transfer server received a signal from invalid upload peer";
  213. return;
  214. }
  215. if( !Settings::instance().fileTransferIsEnabled() )
  216. {
  217. qWarning() << "File Transfer is disabled";
  218. upload_peer->cancelTransfer();
  219. return;
  220. }
  221. FileInfo file_info = fileInfo( file_id );
  222. if( !file_info.isValid() )
  223. {
  224. // Now check file sharing
  225. file_info = FileShare::instance().localFileInfo( file_id );
  226. if( !file_info.isValid() )
  227. {
  228. qWarning() << "File Transfer server received a request of a file not in list";
  229. upload_peer->cancelTransfer();
  230. return;
  231. }
  232. }
  233. if( file_info.password() != file_password )
  234. {
  235. qWarning() << "File Transfer server received a request for the file" << file_info.name() << "but with the wrong password";
  236. upload_peer->cancelTransfer();
  237. return;
  238. }
  239. upload_peer->startUpload( file_info );
  240. }
  241. void FileTransfer::downloadFile( const FileInfo& fi )
  242. {
  243. FileTransferPeer *download_peer = new FileTransferPeer( this );
  244. download_peer->setTransferType( FileTransferPeer::Download );
  245. download_peer->setId( Protocol::instance().newId() );
  246. download_peer->setFileInfo( fi );
  247. download_peer->setInQueue();
  248. m_peers.append( download_peer );
  249. #ifdef BEEBEEP_DEBUG
  250. qDebug() << download_peer->name() << "is scheduled for download";
  251. #endif
  252. if( activeDownloads() < Settings::instance().maxSimultaneousDownloads() )
  253. setupPeer( download_peer, 0 );
  254. }
  255. FileTransferPeer* FileTransfer::peer( VNumber peer_id ) const
  256. {
  257. QList<FileTransferPeer*>::const_iterator it = m_peers.begin();
  258. while( it != m_peers.end() )
  259. {
  260. if( (*it)->id() == peer_id )
  261. return *it;
  262. ++it;
  263. }
  264. qWarning() << "File Transfer server has not found the peer" << peer_id;
  265. return 0;
  266. }
  267. void FileTransfer::peerDestroyed()
  268. {
  269. if( !sender() )
  270. {
  271. qWarning() << "File Transfer is unable to find peer sender of signal destroyed(). List become invalid";
  272. return;
  273. }
  274. if( m_peers.removeOne( (FileTransferPeer*)sender() ) )
  275. qDebug() << "Removing peer from list." << m_peers.size() << "peers remained";
  276. if( isListening() && downloadsInQueue() > 0 )
  277. startNewDownload();
  278. }
  279. bool FileTransfer::cancelTransfer( VNumber peer_id )
  280. {
  281. FileTransferPeer* transfer_peer = peer( peer_id );
  282. if( transfer_peer )
  283. {
  284. qDebug() << "File Transfer server requests to cancel transfer in progress of" << transfer_peer->name();
  285. transfer_peer->cancelTransfer();
  286. return true;
  287. }
  288. qWarning() << "File Transfer server cannot cancel the file transfer because it has not found the peer" << peer_id;
  289. return false;
  290. }
  291. int FileTransfer::activeDownloads() const
  292. {
  293. int active_downloads = 0;
  294. foreach( FileTransferPeer* transfer_peer, m_peers )
  295. {
  296. if( transfer_peer->isDownload() && transfer_peer->isActive() )
  297. active_downloads++;
  298. }
  299. return active_downloads;
  300. }
  301. int FileTransfer::downloadsInQueue() const
  302. {
  303. int downloads_in_queue = 0;
  304. foreach( FileTransferPeer* transfer_peer, m_peers )
  305. {
  306. if( transfer_peer->isDownload() && transfer_peer->isInQueue() )
  307. downloads_in_queue++;
  308. }
  309. return downloads_in_queue;
  310. }
  311. FileTransferPeer* FileTransfer::nextDownloadInQueue() const
  312. {
  313. foreach( FileTransferPeer* transfer_peer, m_peers )
  314. {
  315. if( transfer_peer->isDownload() && transfer_peer->isInQueue() )
  316. return transfer_peer;
  317. }
  318. return 0;
  319. }
  320. void FileTransfer::startNewDownload()
  321. {
  322. if( activeDownloads() >= Settings::instance().maxSimultaneousDownloads() )
  323. return;
  324. FileTransferPeer* download_peer = nextDownloadInQueue();
  325. if( !download_peer )
  326. return;
  327. #ifdef BEEBEEP_DEBUG
  328. qDebug() << download_peer->name() << "is removed from queue and started";
  329. #endif
  330. setupPeer( download_peer, 0 );
  331. }