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