1 //////////////////////////////////////////////////////////////////////
2 //
3 // BeeBEEP Copyright (C) 2010-2021 Marco Mastroddi
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: FileTransfer.cpp 1455 2020-12-23 10:17:53Z mastroddi $
21 //
22 //////////////////////////////////////////////////////////////////////
23 
24 #include "FileShare.h"
25 #include "FileTransfer.h"
26 #include "Random.h"
27 #include "Settings.h"
28 #include "Protocol.h"
29 #include "UserManager.h"
30 
31 
FileTransfer(QObject * parent)32 FileTransfer::FileTransfer( QObject *parent )
33   : QTcpServer( parent ), m_files(), m_peers()
34 {
35 }
36 
startListener()37 bool FileTransfer::startListener()
38 {
39   qDebug() << "Starting File Transfer server";
40 
41   if( !listen( Settings::instance().hostAddressToListen(), static_cast<quint16>(Settings::instance().defaultFileTransferPort() ) ) )
42   {
43     qWarning() << "Unable to bind default file transfer port" << Settings::instance().defaultFileTransferPort();
44     if( !listen( Settings::instance().hostAddressToListen() ) )
45     {
46       qWarning() << "Unable to bind a valid file transfer port";
47       return false;
48     }
49   }
50 
51   qDebug() << "File Transfer server listen" << serverAddress().toString() << serverPort();
52   resetServerFiles();
53   emit listening();
54 
55   if( downloadsInQueue() > 0  )
56     qDebug() << "File Transfer has" << downloadsInQueue() << "files in queue";
57 
58   return true;
59 }
60 
stopListener()61 void FileTransfer::stopListener()
62 {
63   if( isListening() )
64   {
65     close();
66     qDebug() << "File Transfer server closed";
67     foreach( FileTransferPeer* transfer_peer, m_peers )
68     {
69       if( Settings::instance().resumeFileTransfer() )
70         transfer_peer->pauseTransfer( true );
71       else
72         transfer_peer->cancelTransfer();
73     }
74   }
75   else
76     qDebug() << "File Transfer server is not active";
77 }
78 
resetServerFiles()79 void FileTransfer::resetServerFiles()
80 {
81 #ifdef BEEBEEP_DEBUG
82   qDebug() << "File Transfer reset files to"
83            << qPrintable( Settings::instance().localUser().networkAddress().hostAddress().toString() )
84            << serverPort();
85 #endif
86   QList<FileInfo>::iterator it = m_files.begin();
87   while( it != m_files.end() )
88   {
89     (*it).setHostAddress( Settings::instance().localUser().networkAddress().hostAddress() );
90     (*it).setHostPort( serverPort() );
91     ++it;
92   }
93 }
94 
removeFilesToUser(VNumber user_id)95 void FileTransfer::removeFilesToUser( VNumber user_id )
96 {
97   int peer_counter = 0;
98   foreach( FileTransferPeer* transfer_peer, m_peers )
99   {
100     if( transfer_peer->isDownload() && transfer_peer->remoteUserId() == user_id )
101     {
102       transfer_peer->cancelTransfer();
103       peer_counter++;
104     }
105   }
106 
107   if( peer_counter > 0 )
108     qDebug() << peer_counter << "file transfer peers removed for user" << user_id;
109 }
110 
fileInfo(VNumber file_id) const111 FileInfo FileTransfer::fileInfo( VNumber file_id ) const
112 {
113   QList<FileInfo>::const_iterator it = m_files.begin();
114   while( it != m_files.end() )
115   {
116     if( (*it).id() == file_id )
117       return *it;
118     ++it;
119   }
120   return FileInfo();
121 }
122 
fileInfo(const QString & file_absolute_path,const QString chat_private_id) const123 FileInfo FileTransfer::fileInfo( const QString& file_absolute_path, const QString chat_private_id ) const
124 {
125   QList<FileInfo>::const_iterator it = m_files.begin();
126   while( it != m_files.end() )
127   {
128     if( (*it).path() == file_absolute_path && (*it).chatPrivateId() == chat_private_id )
129       return *it;
130     ++it;
131   }
132   return FileInfo();
133 }
134 
removeFile(const QString & file_path)135 void FileTransfer::removeFile( const QString& file_path )
136 {
137   QList<FileInfo>::iterator it = m_files.begin();
138   while( it != m_files.end() )
139   {
140     if( (*it).path() == file_path )
141       it = m_files.erase( it );
142     else
143       ++it;
144   }
145 }
146 
addFile(const QFileInfo & fi,const QString & share_folder,bool to_share_box,const QString & chat_private_id,FileInfo::ContentType content_type,qint64 message_duration)147 FileInfo FileTransfer::addFile( const QFileInfo& fi, const QString& share_folder, bool to_share_box, const QString& chat_private_id, FileInfo::ContentType content_type, qint64 message_duration )
148 {
149   FileInfo file_info = fileInfo( fi.absoluteFilePath(), chat_private_id );
150   if( file_info.isValid() )
151   {
152     QString file_hash = Protocol::instance().fileInfoHash( fi );
153     if( file_info.fileHash() == file_hash )
154       return file_info;
155     else
156       removeFile( fi.absoluteFilePath() );
157   }
158 
159   file_info = Protocol::instance().fileInfo( fi, share_folder, to_share_box, chat_private_id, content_type );
160   file_info.setHostAddress( Settings::instance().localUser().networkAddress().hostAddress() );
161   file_info.setHostPort( serverPort() );
162   file_info.setDuration( message_duration );
163   m_files.append( file_info );
164   return file_info;
165 }
166 
addFileInfoList(const QList<FileInfo> & file_info_list)167 void FileTransfer::addFileInfoList( const QList<FileInfo>& file_info_list )
168 {
169   foreach( FileInfo fi, file_info_list )
170   {
171     if( !fileInfo( fi.id() ).isValid() )
172       m_files.append( fi );
173   }
174 }
175 
incomingConnection(qintptr socket_descriptor)176 void FileTransfer::incomingConnection( qintptr socket_descriptor )
177 {
178   FileTransferPeer *upload_peer = new FileTransferPeer( this );
179   upload_peer->setTransferType( FileInfo::Upload );
180   upload_peer->setId( Protocol::instance().newId() );
181   m_peers.append( upload_peer );
182   setupPeer( upload_peer, socket_descriptor, serverPort() );
183 }
184 
setupPeer(FileTransferPeer * transfer_peer,qintptr socket_descriptor,quint16 server_port)185 void FileTransfer::setupPeer( FileTransferPeer* transfer_peer, qintptr socket_descriptor, quint16 server_port )
186 {
187   if( transfer_peer->isInQueue() )
188   {
189 #ifdef BEEBEEP_DEBUG
190     qDebug() << qPrintable( transfer_peer->name() ) << "is removed from queue";
191 #endif
192     transfer_peer->removeFromQueue();
193   }
194 
195 #ifdef BEEBEEP_DEBUG
196   qDebug() << qPrintable( transfer_peer->name() ) << "starts its connection. Active downloads:" << activeDownloads();
197 #endif
198   if( !transfer_peer->isDownload() )
199   {
200     connect( transfer_peer, SIGNAL( fileUploadRequest( const FileInfo& ) ), this, SLOT( checkUploadRequest( const FileInfo& ) ) );
201     connect( transfer_peer, SIGNAL( message( VNumber, VNumber, const FileInfo&, const QString&, FileTransferPeer::TransferState ) ), this, SIGNAL( message( VNumber, VNumber, const FileInfo&, const QString&, FileTransferPeer::TransferState ) ) );
202   }
203 
204   connect( transfer_peer, SIGNAL( progress( VNumber, VNumber, const FileInfo&, FileSizeType, qint64 ) ), this, SIGNAL( progress( VNumber, VNumber, const FileInfo&, FileSizeType, qint64 ) ) );
205   connect( transfer_peer, SIGNAL( operationCompleted() ), this, SLOT( deletePeer() ) );
206 
207   transfer_peer->setConnectionDescriptor( socket_descriptor, server_port );
208   int delay = Random::number32( 1, 9 ) * 100;
209 #ifdef BEEBEEP_DEBUG
210   qDebug() << qPrintable( transfer_peer->name() ) << "starts in" << delay << "ms";
211 #endif
212   QTimer::singleShot( delay, transfer_peer, SLOT( startConnection() ) );
213 }
214 
215 
checkUploadRequest(const FileInfo & file_info_to_check)216 void FileTransfer::checkUploadRequest( const FileInfo& file_info_to_check )
217 {
218 #ifdef BEEBEEP_DEBUG
219   qDebug() << "Checking upload request:" << file_info_to_check.id() << file_info_to_check.password();
220   qDebug() << "FileInfo name:" << file_info_to_check.name();
221   qDebug() << "FileInfo path:" << file_info_to_check.path();
222   qDebug() << "FileInfo share folder:" << file_info_to_check.shareFolder();
223   qDebug() << "FileInfo is share box:" << file_info_to_check.isInShareBox();
224   qDebug() << "FileInfo is folder:" << file_info_to_check.isFolder();
225 #endif
226 
227   FileTransferPeer *upload_peer = qobject_cast<FileTransferPeer*>( sender() );
228   if( !upload_peer )
229   {
230     qWarning() << "File Transfer server received a signal from invalid upload peer for file" << qPrintable( file_info_to_check.name() );
231     return;
232   }
233 
234   if( !Settings::instance().enableFileTransfer() )
235   {
236     qWarning() << "File Transfer is disabled: unable to upload file" << qPrintable( file_info_to_check.name() );
237     upload_peer->cancelTransfer();
238     return;
239   }
240 
241   if( !Settings::instance().isFileExtensionAllowedInFileTransfer( file_info_to_check.suffix() ) )
242   {
243     qWarning() << "File Transfer is not allowed to upload file" << qPrintable( file_info_to_check.name() ) << "with extension" << file_info_to_check.suffix();
244     upload_peer->cancelTransfer();
245     return;
246   }
247 
248   FileInfo file_info;
249 
250   if( file_info_to_check.isInShareBox() )
251   {
252     if( !Settings::instance().enableFileSharing() )
253     {
254       qWarning() << "BeeBOX file upload request refused (file sharing disabled):" << file_info_to_check.name();
255       upload_peer->cancelTransfer();
256       return;
257     }
258 
259     if( !Settings::instance().useShareBox() )
260     {
261       qWarning() << "BeeBOX file upload request refused (sharebox disabled):" << file_info_to_check.name();
262       upload_peer->cancelTransfer();
263       return;
264     }
265 
266     file_info = fileInfo( file_info_to_check.id() );
267     if( !file_info.isValid() )
268     {
269       QString file_path;
270       if( file_info_to_check.shareFolder().isEmpty() )
271         file_path = QString( "%1/%2" ).arg( Settings::instance().shareBoxPath() )
272                                       .arg( file_info_to_check.name() );
273       else
274         file_path = QString( "%1/%2/%3" ).arg( Settings::instance().shareBoxPath() )
275                                          .arg( file_info_to_check.shareFolder() )
276                                          .arg( file_info_to_check.name() );
277 
278       QFileInfo share_box_file_info( file_path );
279       if( !share_box_file_info.exists() )
280       {
281         qWarning() << "BeeBOX file upload request refused (not exists):" << file_path;
282         upload_peer->cancelTransfer();
283       }
284 
285       if( !share_box_file_info.isReadable() )
286       {
287         qWarning() << "BeeBOX file upload request refused (not readable):" << file_path;
288         upload_peer->cancelTransfer();
289       }
290 
291       file_info = Protocol::instance().fileInfo( share_box_file_info, "", true, "", FileInfo::File );
292     }
293   }
294   else
295   {
296     file_info = fileInfo( file_info_to_check.id() );
297 
298     if( !file_info.isValid() && !Settings::instance().disableFileSharing() )
299     {
300       // Now check file sharing
301       file_info = FileShare::instance().localFileInfo( file_info_to_check.id() );
302     }
303 
304     if( !file_info.isValid() )
305     {
306       qWarning() << "File Transfer server received a request of a file not in list (or file sharing is disabled)";
307       upload_peer->cancelTransfer();
308       return;
309     }
310 
311     if( file_info.password() != file_info_to_check.password() )
312     {
313       qWarning() << "File Transfer server received a request for the file" << file_info.name() << "but with the wrong password";
314       upload_peer->cancelTransfer();
315       return;
316     }
317   }
318 
319   if( file_info_to_check.startingPosition() == file_info.size() )
320   {
321     upload_peer->skipTransfer();
322     return;
323   }
324 
325   if( Settings::instance().resumeFileTransfer() )
326     file_info.setStartingPosition( file_info_to_check.startingPosition() );
327   else
328     file_info.setStartingPosition( 0 );
329   upload_peer->startUpload( file_info );
330 }
331 
downloadFile(VNumber from_user_id,const FileInfo & fi)332 void FileTransfer::downloadFile( VNumber from_user_id, const FileInfo& fi )
333 {
334   FileTransferPeer *download_peer = new FileTransferPeer( this );
335   download_peer->setTransferType( FileInfo::Download );
336   download_peer->setId( Protocol::instance().newId() );
337   download_peer->setFileInfo( FileInfo::Download, fi );
338   download_peer->setRemoteUserId( from_user_id );
339 
340   connect( download_peer, SIGNAL( message( VNumber, VNumber, const FileInfo&, const QString&, FileTransferPeer::TransferState ) ), this, SIGNAL( message( VNumber, VNumber, const FileInfo&, const QString&, FileTransferPeer::TransferState ) ) );
341   // connect before setInQueue to send message to GUI
342   download_peer->setInQueue();
343 
344   m_peers.append( download_peer );
345 
346   if( activeDownloads() < Settings::instance().maxSimultaneousDownloads() )
347     setupPeer( download_peer, 0 );
348 }
349 
peer(VNumber peer_id) const350 FileTransferPeer* FileTransfer::peer( VNumber peer_id ) const
351 {
352   QList<FileTransferPeer*>::const_iterator it = m_peers.begin();
353   while( it != m_peers.end() )
354   {
355     if( (*it)->id() == peer_id )
356       return *it;
357     ++it;
358   }
359   return Q_NULLPTR;
360 }
361 
deletePeer()362 void FileTransfer::deletePeer()
363 {
364   if( !sender() )
365   {
366     qWarning() << "File Transfer is unable to find peer sender of signal destroyed(). List become invalid";
367     return;
368   }
369 
370   FileTransferPeer* sender_peer = dynamic_cast<FileTransferPeer*>( sender() );
371   if( m_peers.removeOne( sender_peer ) )
372   {
373     qDebug() << "Removed peer from list." << m_peers.size() << "peers remained";
374     sender_peer->deleteLater();
375   }
376 
377   if( isListening() && downloadsInQueue() > 0 )
378     QTimer::singleShot( 0, this, SLOT( startNewDownload() ) ); // to next main loop avoiding a crash
379 }
380 
cancelTransfer(VNumber peer_id)381 bool FileTransfer::cancelTransfer( VNumber peer_id )
382 {
383   FileTransferPeer* transfer_peer = peer( peer_id );
384   if( transfer_peer )
385   {
386     transfer_peer->cancelTransfer();
387     if( transfer_peer->isDownload() )
388       transfer_peer->removePartiallyDownloadedFile();
389     return true;
390   }
391   qWarning() << "File Transfer server cannot cancel the file transfer because it has not found the peer" << peer_id;
392   return false;
393 }
394 
pauseTransfer(VNumber peer_id)395 bool FileTransfer::pauseTransfer( VNumber peer_id )
396 {
397   FileTransferPeer* transfer_peer = peer( peer_id );
398   if( transfer_peer )
399   {
400     if( transfer_peer->canPauseTransfer() )
401     {
402       transfer_peer->pauseTransfer( false );
403       return true;
404     }
405     else
406       return false;
407   }
408   qWarning() << "File Transfer server cannot pause the file transfer because it has not found the peer" << peer_id;
409   return false;
410 }
411 
activeDownloads() const412 int FileTransfer::activeDownloads() const
413 {
414   int active_downloads = 0;
415   foreach( FileTransferPeer* transfer_peer, m_peers )
416   {
417     if( transfer_peer->isDownload() && transfer_peer->isActive() )
418       active_downloads++;
419   }
420   return active_downloads;
421 }
422 
downloadsInQueue() const423 int FileTransfer::downloadsInQueue() const
424 {
425   int downloads_in_queue = 0;
426   foreach( FileTransferPeer* transfer_peer, m_peers )
427   {
428     if( transfer_peer->isDownload() && transfer_peer->isInQueue() )
429       downloads_in_queue++;
430   }
431   return downloads_in_queue;
432 }
433 
nextDownloadInQueue() const434 FileTransferPeer* FileTransfer::nextDownloadInQueue() const
435 {
436   foreach( FileTransferPeer* transfer_peer, m_peers )
437   {
438     if( transfer_peer->isDownload() && transfer_peer->isInQueue() )
439     {
440       if( transfer_peer->remoteUserId() != ID_INVALID )
441       {
442         User remote_user = UserManager::instance().findUser( transfer_peer->remoteUserId() );
443         if( remote_user.isValid() && remote_user.isStatusConnected() )
444           return transfer_peer;
445       }
446     }
447   }
448   return Q_NULLPTR;
449 }
450 
startNewDownload()451 void FileTransfer::startNewDownload()
452 {
453   if( activeDownloads() >= Settings::instance().maxSimultaneousDownloads() )
454     return;
455 
456   FileTransferPeer* download_peer = nextDownloadInQueue();
457   if( !download_peer )
458     return;
459 
460 #ifdef BEEBEEP_DEBUG
461   qDebug() << download_peer->name() << "is removed from queue and started";
462 #endif
463 
464   setupPeer( download_peer, 0 );
465 }
466 
onTickEvent(int ticks)467 void FileTransfer::onTickEvent( int ticks )
468 {
469   if( !isListening() )
470     return;
471 
472   foreach( FileTransferPeer* transfer_peer, m_peers )
473     transfer_peer->onTickEvent( ticks );
474 }
475