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: CoreFileTransfer.cpp 1462 2021-01-02 12:54:56Z mastroddi $
21 //
22 //////////////////////////////////////////////////////////////////////
23
24 #include "AudioManager.h"
25 #include "BeeApplication.h"
26 #include "BeeUtils.h"
27 #include "BuildFileList.h"
28 #include "BuildFileShareList.h"
29 #include "ChatManager.h"
30 #include "Connection.h"
31 #include "Core.h"
32 #include "FileShare.h"
33 #include "FileTransferPeer.h"
34 #include "IconManager.h"
35 #include "Protocol.h"
36 #include "Settings.h"
37 #include "UserManager.h"
38
39
startFileTransferServer()40 bool Core::startFileTransferServer()
41 {
42 if( mp_fileTransfer->isActive() )
43 {
44 #ifdef BEEBEEP_DEBUG
45 qDebug() << "Starting File Transfer server but it is already started";
46 #endif
47 return true;
48 }
49
50 if( !mp_fileTransfer->startListener() )
51 {
52 QString icon_html = IconManager::instance().toHtml( "warning.png", "*F*" );
53 dispatchSystemMessage( ID_DEFAULT_CHAT, ID_LOCAL_USER,
54 tr( "%1 Unable to start file transfer server: bind address/port failed." ).arg( icon_html ),
55 DispatchToChat, ChatMessage::System, false );
56 return false;
57 }
58
59 return true;
60 }
61
stopFileTransferServer()62 void Core::stopFileTransferServer()
63 {
64 mp_fileTransfer->stopListener();
65 Protocol::instance().createFileShareListMessage( FileShare::instance().local(), -1 );
66 }
67
downloadFile(VNumber user_id,const FileInfo & fi,bool show_message)68 bool Core::downloadFile( VNumber user_id, const FileInfo& fi, bool show_message )
69 {
70 User u = UserManager::instance().findUser( user_id );
71 if( !u.isValid() )
72 {
73 qWarning() << "Unable to find user" << user_id << "to download file" << fi.name();
74 return false;
75 }
76
77 QString icon_html;
78 Chat chat_to_show_message = ChatManager::instance().findChatByPrivateId( fi.chatPrivateId(), false, u.id() );
79
80 if( !isUserConnected( u.id() ) )
81 {
82 if( show_message )
83 {
84 icon_html = IconManager::instance().toHtml( "red-ball.png", "*F*" );
85 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to download %2 from %3: user is offline." ).arg( icon_html, fi.name(), Bee::userNameToShow( u, true ) ),
86 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
87 }
88 qWarning() << "Unable to download" << fi.name() << "from" << u.path() << "because user is offline";
89 return false;
90 }
91
92 QFileInfo file_info( fi.path() );
93 QDir folder_path = file_info.absoluteDir();
94 if( !folder_path.exists() )
95 {
96 if( !folder_path.mkpath( "." ) )
97 {
98 if( show_message )
99 {
100 icon_html = IconManager::instance().toHtml( "red-ball.png", "*F*" );
101 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to download %2 from %3: folder %4 cannot be created." )
102 .arg( icon_html, fi.name(), Bee::userNameToShow( u, true ), folder_path.absolutePath() ),
103 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
104 }
105 qWarning() << "Unable to download" << qPrintable( fi.name() ) << "because folder" << qPrintable( folder_path.absolutePath() ) << "can not be created";
106 return false;
107 }
108 }
109
110 if( !Settings::instance().isFileExtensionAllowedInFileTransfer( file_info.suffix() ) )
111 {
112 if( show_message )
113 {
114 icon_html = IconManager::instance().toHtml( "red-ball.png", "*F*" );
115 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to download %2 from %3: file extension '%4' is not allowed." )
116 .arg( icon_html, fi.name(), Bee::userNameToShow( u, true ), file_info.suffix().toUpper() ),
117 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
118 }
119 qWarning() << "Unable to download" << qPrintable( fi.path() ) << "because file extension" << file_info.suffix() << "is not allowed";
120 return false;
121 }
122
123 if( show_message )
124 {
125 icon_html = IconManager::instance().toHtml( "download.png", "*F*" );
126 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Downloading %2 from %3." )
127 .arg( icon_html, fi.name(), Bee::userNameToShow( u, true ) ),
128 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
129 }
130
131 qDebug() << "Downloading file" << qPrintable( fi.path() ) << "from user" << qPrintable( u.path() );
132 mp_fileTransfer->downloadFile( u.id(), fi );
133 return true;
134 }
135
checkFileTransferMessage(VNumber peer_id,VNumber user_id,const FileInfo & fi,const QString & msg,FileTransferPeer::TransferState ft_state)136 void Core::checkFileTransferMessage( VNumber peer_id, VNumber user_id, const FileInfo& fi, const QString& msg, FileTransferPeer::TransferState ft_state )
137 {
138 User u = UserManager::instance().findUser( user_id );
139 if( !u.isValid() )
140 {
141 qWarning() << "Unable to find user" << user_id << "for the file transfer" << qPrintable( fi.name() ) << "in Core::checkFileTransferMessage(...)";
142 return;
143 }
144
145 QString icon_html = IconManager::instance().toHtml( fi.isDownload() ? "download.png" : "upload.png", "*F*" );
146 QString action_txt = fi.isDownload() ? tr( "Download" ) : tr( "Upload" );
147 QString sys_msg = "";
148 QString sys_msg_img_preview = "";
149 QString sys_msg_open_file = "";
150 QString sys_msg_play_voice_chat = "";
151 QString chat_voice_msg_html = "";
152 ChatMessage chat_voice_msg;
153 QUrl file_url = QUrl::fromLocalFile( fi.path() );
154
155 if( FileTransferPeer::stateIsStopped( ft_state ) )
156 sys_msg = QString( "%1 %2 %3 %4 %5: %6." ).arg( icon_html, action_txt, fi.isDownload() ? fi.name() : QString( "<a href=\"%1\">%2</a>" ).arg( file_url.toString(), fi.name() ), fi.isDownload() ? tr( "from" ) : tr( "to" ), Bee::userNameToShow( u, true ), Bee::lowerFirstLetter( msg ) );
157
158 if( fi.isDownload() && ft_state == FileTransferPeer::Completed )
159 {
160 FileShare::instance().addDownloadedFile( fi );
161 if( Bee::isFileTypeImage( fi.suffix() ) )
162 {
163 QString img_preview_path = Bee::imagePreviewPath( fi.path() );
164 if( img_preview_path.isEmpty() )
165 qWarning() << "Unable to show image preview of the file" << fi.path();
166 else
167 sys_msg_img_preview = QString( "<br><div align=center><a href=\"%1\"><img src=\"%2\"></a></div>" )
168 .arg( file_url.toString(), QUrl::fromLocalFile( img_preview_path ).toString() );
169 // I have to add <br> to center the image on the first display (maybe Qt bug?)
170 }
171
172 if( fi.isVoiceMessage() )
173 {
174 QString sys_txt = tr( "%1 sent a voice message." ).arg( Bee::userNameToShow( u, true ) );
175 QString html_audio_icon = IconManager::instance().toHtml( "voice-message.png", "*v*" );
176 #ifdef BEEBEEP_USE_VOICE_CHAT
177 file_url.setScheme( FileInfo::urlSchemeVoiceMessage() );
178 #endif
179 sys_msg_play_voice_chat = QString( "%1 %2 <a href=\"%3\">%4</a>." ).arg( html_audio_icon, sys_txt, file_url.toString(), tr( "Listen" ) );
180 chat_voice_msg_html = QString( "[ <a href=\"%1\">%2</a> ]%3%4" )
181 .arg( file_url.toString() )
182 .arg( tr( "voice message" ) )
183 .arg( fi.duration() > 0 ? QString( " (%1) " ).arg( Bee::timeToString( fi.duration() ) ) : QString( " " ) )
184 .arg( html_audio_icon );
185 // New feature: adding save as to avoid cache deleted ... select voice message folder
186 }
187 else
188 {
189 sys_msg_open_file = QString( "%1 %2" ).arg( icon_html ).arg( tr( "%1 sent %2." ).arg( Bee::userNameToShow( u, true ) ).arg( QString( "<a href=\"%1\">%2</a>" ).arg( file_url.toString(), fi.name() ) ) );
190 file_url.setScheme( FileInfo::urlSchemeShowFileInFolder() );
191 sys_msg_open_file += QString( " " );
192 sys_msg_open_file += tr( "Open %1." ).arg( QString( "<a href=\"%1\">%2</a>" ).arg( file_url.toString(), tr( "folder" ) ) );
193 }
194 }
195
196 Chat chat_to_show_message = ChatManager::instance().findChatByPrivateId( fi.chatPrivateId(), false, u.id() );
197 if( chat_to_show_message.isValid() )
198 {
199 if( fi.isDownload() && ft_state == FileTransferPeer::Completed )
200 {
201 if( !fi.isInShareBox() )
202 {
203 chat_to_show_message.addUnreadMessage();
204 ChatManager::instance().setChat( chat_to_show_message );
205 }
206 if( !chat_voice_msg_html.isEmpty() )
207 chat_voice_msg = ChatMessage( fi.isDownload() ? u.id() : ID_LOCAL_USER, chat_voice_msg_html, ChatMessage::Voice, true );
208 }
209 }
210
211 if( fi.isVoiceMessage() )
212 {
213 if( fi.isDownload() )
214 {
215 if( chat_voice_msg.isValid() )
216 dispatchToChat( chat_voice_msg, chat_to_show_message.id() );
217 else
218 dispatchSystemMessage( ID_DEFAULT_CHAT, u.id(), sys_msg_play_voice_chat, DispatchToDefaultAndPrivateChat, ChatMessage::Voice, true );
219 }
220 }
221 else
222 {
223 if( !sys_msg.isEmpty() )
224 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), sys_msg, chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
225 if( !sys_msg_img_preview.isEmpty() )
226 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), sys_msg_img_preview, chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::ImagePreview, true );
227 if( !sys_msg_open_file.isEmpty() )
228 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), sys_msg_open_file, chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, true );
229 }
230
231 emit fileTransferMessage( peer_id, u, fi, msg, ft_state );
232 }
233
checkFileTransferProgress(VNumber peer_id,VNumber user_id,const FileInfo & fi,FileSizeType bytes,qint64 elapsed_time)234 void Core::checkFileTransferProgress( VNumber peer_id, VNumber user_id, const FileInfo& fi, FileSizeType bytes, qint64 elapsed_time )
235 {
236 User u = UserManager::instance().findUser( user_id );
237 if( !u.isValid() )
238 {
239 qWarning() << "Unable to find user" << user_id << "for the file transfer" << fi.name() << "in Core::checkFileTransferProgress(...)";
240 return;
241 }
242 emit fileTransferProgress( peer_id, u, fi, bytes, elapsed_time );
243 }
244
showFileUploadPreviewInChat(const Chat & c,const QString & file_path)245 bool Core::showFileUploadPreviewInChat( const Chat& c, const QString& file_path )
246 {
247 QStringList sl;
248 sl.append( file_path );
249 return showFilesUploadPreviewInChat( c, sl );
250 }
251
showFilesUploadPreviewInChat(const Chat & c,const QStringList & file_paths)252 bool Core::showFilesUploadPreviewInChat( const Chat& c, const QStringList& file_paths )
253 {
254 if( !c.isValid() )
255 return false;
256
257 QStringList preview_list;
258 foreach( QString file_path, file_paths )
259 {
260 QFileInfo fi( file_path );
261 if( Bee::isFileTypeImage( fi.suffix() ) )
262 {
263 QString img_preview_path = Bee::imagePreviewPath( file_path );
264 #ifdef BEEBEEP_DEBUG
265 qDebug() << "Showing image preview"<< img_preview_path << "of file" << qPrintable( file_path );
266 #endif
267 if( !img_preview_path.isEmpty() )
268 {
269 QUrl file_url( file_path );
270 QString img_preview = QString( "<a href=\"%1\"><img src=\"%2\"></a>" ).arg( file_url.toString(), QUrl::fromLocalFile( img_preview_path ).toString() );
271 preview_list.append( img_preview );
272 }
273 else
274 qWarning() << "Unable to show image preview of the file" << qPrintable( file_path );
275 }
276 }
277
278 if( preview_list.isEmpty() )
279 return false;
280
281 QString preview_msg = "<br><div align=center>";
282 foreach( QString preview_item , preview_list)
283 preview_msg += QString( " %1 " ).arg( preview_item );
284 preview_msg += QString( "</div>" );
285 dispatchSystemMessage( c.id(), ID_LOCAL_USER, preview_msg, DispatchToChat, ChatMessage::ImagePreview, true );
286 return true;
287 }
288
sendFilesFromChat(VNumber chat_id,const QStringList & file_path_list)289 int Core::sendFilesFromChat( VNumber chat_id, const QStringList& file_path_list )
290 {
291 if( file_path_list.isEmpty() )
292 return 0;
293
294 Chat c = ChatManager::instance().chat( chat_id );
295 if( !c.isValid() )
296 {
297 qWarning() << "Unable to find chat" << chat_id << "for sending files" << qPrintable( file_path_list.join( ", " ) );
298 return 0;
299 }
300
301 QString icon_html = IconManager::instance().toHtml( "red-ball.png", "*F*" );
302 if( !Settings::instance().enableFileTransfer() )
303 {
304 dispatchSystemMessage( chat_id, ID_LOCAL_USER, tr( "%1 Unable to send %2. File transfer is disabled." ).arg( icon_html, file_path_list.join( ", " ) ), DispatchToChat, ChatMessage::FileTransfer, false );
305 return 0;
306 }
307
308 int files_sent = 0;
309 QStringList to_users;
310 QStringList file_sent_list;
311 UserList chat_members = c.isDefault() ? UserManager::instance().userList() : UserManager::instance().userList().fromUsersId( c.usersId() );
312 foreach( QString file_path, file_path_list )
313 {
314 foreach( User u, chat_members.toList() )
315 {
316 if( !u.isLocal() )
317 {
318 if( sendFileToUser( u, file_path, "", false, c ) )
319 {
320 files_sent++;
321 QString user_name = Bee::userNameToShow( u, true );
322 if( !to_users.contains( user_name ) )
323 to_users.append( user_name );
324 if( !file_sent_list.contains( file_path ) )
325 file_sent_list.append( file_path );
326 }
327 }
328 }
329 }
330
331 showFilesUploadPreviewInChat( c, file_sent_list );
332 return files_sent;
333 }
334
sendFile(VNumber user_id,const QString & file_path,const QString & share_folder,bool to_share_box,VNumber chat_id)335 bool Core::sendFile( VNumber user_id, const QString& file_path, const QString& share_folder, bool to_share_box, VNumber chat_id )
336 {
337 QString icon_html = IconManager::instance().toHtml( "red-ball.png", "*F*" );
338 if( !Settings::instance().enableFileTransfer() )
339 {
340 dispatchSystemMessage( chat_id != ID_INVALID ? chat_id : ID_DEFAULT_CHAT, user_id, tr( "%1 Unable to send %2. File transfer is disabled." ).arg( icon_html, file_path ),
341 chat_id != ID_INVALID ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
342 return false;
343 }
344
345 User u = UserManager::instance().findUser( user_id );
346 if( !u.isValid() )
347 {
348 qWarning() << "Unable to find user" << user_id << "for sending file" << file_path << "in Core::sendFile(...)";
349 return false;
350 }
351
352 Chat c = ChatManager::instance().chat( chat_id );
353 if( !c.isValid() )
354 c = ChatManager::instance().privateChatForUser( user_id );
355 bool file_sent = sendFileToUser( u, file_path, share_folder, to_share_box, c );
356 if( file_sent && c.isValid() )
357 showFileUploadPreviewInChat( c, file_path );
358 return file_sent;
359 }
360
sendFileToUser(const User & u,const QString & file_path,const QString & share_folder,bool to_share_box,const Chat & chat_selected)361 bool Core::sendFileToUser( const User&u, const QString& file_path, const QString& share_folder, bool to_share_box, const Chat& chat_selected )
362 {
363 if( u.isLocal() )
364 return false;
365
366 QString icon_html = IconManager::instance().toHtml( "red-ball.png", "*F*" );
367
368 QFileInfo file( file_path );
369 if( !file.isDir() && !Settings::instance().isFileExtensionAllowedInFileTransfer( file.suffix() ) )
370 {
371 dispatchSystemMessage( chat_selected.isValid() ? chat_selected.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to send %2 to %3: file extension '%4' is not allowed." ).arg( icon_html, file_path, Bee::userNameToShow( u, true ), file.suffix().toUpper() ),
372 chat_selected.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
373 return false;
374 }
375
376 if( !file.exists() )
377 {
378 dispatchSystemMessage( chat_selected.isValid() ? chat_selected.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to send %2 to %3: file not found." ).arg( icon_html, file_path, Bee::userNameToShow( u, true ) ),
379 chat_selected.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
380 return false;
381 }
382
383 if( !file.isDir() && file.size() <= 0 )
384 {
385 dispatchSystemMessage( chat_selected.isValid() ? chat_selected.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to send %2 to %3: file is empty." ).arg( icon_html, file_path, Bee::userNameToShow( u, true ) ),
386 chat_selected.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
387 return false;
388 }
389
390 if( !isUserConnected( u.id() ) )
391 {
392 dispatchSystemMessage( chat_selected.isValid() ? chat_selected.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to send %2 to %3: user is offline." ).arg( icon_html, file_path, Bee::userNameToShow( u, true ) ),
393 chat_selected.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
394 return false;
395 }
396
397 bool file_sent = false;
398 if( file.isDir() )
399 {
400 if( sendFolder( u, file, chat_selected.privateId() ) )
401 file_sent = true;
402 else
403 dispatchSystemMessage( chat_selected.isValid() ? chat_selected.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 %2 is a folder. You can share it." ).arg( icon_html, file.fileName() ),
404 chat_selected.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
405 }
406 else
407 {
408 FileInfo fi = mp_fileTransfer->addFile( file, share_folder, to_share_box, chat_selected.privateId(), FileInfo::File, -1 );
409 Connection* c = connection( u.id() );
410 if( c )
411 {
412 icon_html = IconManager::instance().toHtml( "upload.png", "*F*" );
413 Message m = Protocol::instance().fileInfoToMessage( fi, c->protocolVersion() );
414 if( c->sendMessage( m ) )
415 {
416 qDebug() << "Sending file" << fi.path() << "to" << qPrintable( u.path() );
417 file_sent = true;
418 QUrl file_url = QUrl::fromLocalFile( fi.path() );
419 QString msg = tr( "%1 Sending %2 to %3." ).arg( icon_html, QString( "<a href=\"%1\">%2</a>" ).arg( file_url.toString(), fi.name() ), Bee::userNameToShow( u, true ) );
420 file_url.setScheme( FileInfo::urlSchemeShowFileInFolder() );
421 msg += QString( " " );
422 msg += tr( "Open %1." ).arg( QString( "<a href=\"%1\">%2</a>" ).arg( file_url.toString(), tr( "folder" ) ) );
423 dispatchSystemMessage( chat_selected.isValid() ? chat_selected.id() : ID_DEFAULT_CHAT, u.id(), msg,
424 chat_selected.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, true );
425 }
426 }
427
428 if( !file_sent )
429 {
430 dispatchSystemMessage( chat_selected.isValid() ? chat_selected.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 Unable to send %2: %3 is not connected." )
431 .arg( icon_html ).arg( fi.name() ).arg( Bee::userNameToShow( u, true ) ),
432 chat_selected.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
433 }
434 }
435 return file_sent;
436 }
437
cancelFileTransfer(VNumber peer_id)438 void Core::cancelFileTransfer( VNumber peer_id )
439 {
440 qDebug() << "Core received a request for cancelling file transfer id" << peer_id;
441 mp_fileTransfer->cancelTransfer( peer_id );
442 }
443
pauseFileTransfer(VNumber peer_id)444 void Core::pauseFileTransfer( VNumber peer_id )
445 {
446 qDebug() << "Core received a request for pausing file transfer id" << peer_id;
447 if( !mp_fileTransfer->pauseTransfer( peer_id ) )
448 mp_fileTransfer->cancelTransfer( peer_id );
449 }
450
refuseToDownloadFile(VNumber user_id,const FileInfo & fi)451 void Core::refuseToDownloadFile( VNumber user_id, const FileInfo& fi )
452 {
453 User u = UserManager::instance().findUser( user_id );
454
455 if( !u.isValid() )
456 {
457 qWarning() << "Unable to find user" << user_id << "to refuse file" << qPrintable( fi.name() );
458 return;
459 }
460
461 qDebug() << "Download refused:" << qPrintable( fi.name() ) << "from" << qPrintable( u.path() );
462
463 Chat chat_to_show_message = ChatManager::instance().findChatByPrivateId( fi.chatPrivateId(), false, user_id );
464 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 You have refused to download %2 from %3." )
465 .arg( IconManager::instance().toHtml( "red-ball.png", "*F*" ), fi.name(), Bee::userNameToShow( u, true ) ),
466 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
467
468 Connection* c = connection( u.id() );
469 if( !c )
470 {
471 qDebug() << qPrintable( u.path() ) << "is not connected. Unable to send file download refused message";
472 return;
473 }
474
475 if( c->protocolVersion() > 1 )
476 {
477 Message m = Protocol::instance().fileInfoRefusedToMessage( fi, c->protocolVersion() );
478 c->sendMessage( m );
479 }
480 }
481
refuseToDownloadFolder(VNumber user_id,const QString & folder_name,const QString & chat_private_id)482 void Core::refuseToDownloadFolder( VNumber user_id, const QString& folder_name, const QString& chat_private_id )
483 {
484 User u = UserManager::instance().findUser( user_id );
485
486 if( !u.isValid() )
487 {
488 qWarning() << "Unable to find user" << user_id << "to refuse folder" << folder_name;
489 return;
490 }
491
492 Chat chat_to_show_message = ChatManager::instance().findChatByPrivateId( chat_private_id, false, user_id );
493 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 You have refused to download folder %2 from %3." )
494 .arg( IconManager::instance().toHtml( "red-ball.png", "*F*" ), folder_name, Bee::userNameToShow( u, true ) ),
495 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
496
497 Message m = Protocol::instance().folderRefusedToMessage( folder_name, chat_private_id );
498
499 Connection* c = connection( u.id() );
500 if( !c )
501 {
502 qDebug() << u.path() << "is not connected. Unable to send folder download refused message";
503 return;
504 }
505
506 if( c->protocolVersion() > 1 )
507 c->sendMessage( m );
508 }
509
onFileTransferServerListening()510 void Core::onFileTransferServerListening()
511 {
512 createLocalShareMessage();
513 buildLocalShareList();
514 }
515
sendFileShareRequestToAll()516 void Core::sendFileShareRequestToAll()
517 {
518 Message file_share_request_message = Protocol::instance().fileShareRequestMessage();
519 foreach( Connection* c, m_connections )
520 {
521 if( !FileShare::instance().userHasFileShareList( c->userId() ) )
522 c->sendMessage( file_share_request_message );
523 }
524 }
525
sendFileShareListTo(VNumber user_id)526 void Core::sendFileShareListTo( VNumber user_id )
527 {
528 Connection* c = connection( user_id );
529 if( c )
530 c->sendMessage( Protocol::instance().fileShareListMessage() );
531 else
532 qWarning() << user_id << "is not a valid user id. Unable to send share list message";
533 }
534
sendFileShareListToAll()535 void Core::sendFileShareListToAll()
536 {
537 if( !isConnected() )
538 return;
539
540 Message share_list_message = Protocol::instance().fileShareListMessage();
541
542 foreach( Connection* c, m_connections )
543 c->sendMessage( share_list_message );
544 }
545
addPathToShare(const QString & share_path)546 void Core::addPathToShare( const QString& share_path )
547 {
548 QFileInfo fi( share_path );
549 if( fi.isDir() )
550 {
551 BuildFileShareList *bfsl = new BuildFileShareList;
552 bfsl->setFolderPath( Bee::convertToNativeFolderSeparator( share_path ) );
553 connect( bfsl, SIGNAL( listCompleted() ), this, SLOT( addListToLocalShare() ) );
554 if( beeApp )
555 beeApp->addJob( bfsl );
556 QMetaObject::invokeMethod( bfsl, "buildList", Qt::QueuedConnection );
557 }
558 else
559 {
560 if( Protocol::instance().fileCanBeShared( fi ) )
561 {
562 #ifdef BEEBEEP_DEBUG
563 qDebug() << "Adding to file sharing" << share_path;
564 #endif
565 FileInfo file_info = Protocol::instance().fileInfo( fi, "", false, "", FileInfo::File );
566 FileShare::instance().addToLocal( file_info );
567
568 if( m_shareListToBuild > 0 )
569 m_shareListToBuild--;
570
571 if( m_shareListToBuild == 0 )
572 {
573 createLocalShareMessage();
574 emit localShareListAvailable();
575 }
576 }
577 }
578 }
579
addListToLocalShare()580 void Core::addListToLocalShare()
581 {
582 if( m_shareListToBuild > 0 )
583 m_shareListToBuild--;
584
585 BuildFileShareList *bfsl = qobject_cast<BuildFileShareList*>( sender() );
586 if( !bfsl )
587 {
588 qWarning() << "Core received a signal from invalid BuildFileShareList instance";
589 emit localShareListAvailable();
590 return;
591 }
592
593 int num_files = FileShare::instance().addToLocal( bfsl->folderPath(), bfsl->shareList() );
594
595 QString share_status;
596
597 if( num_files != bfsl->shareList().size() )
598 share_status = tr( "%1 is added to file sharing with only %2 of %3 files (%4 limit reached)" )
599 .arg( bfsl->folderPath() )
600 .arg( num_files )
601 .arg( bfsl->shareList().size() )
602 .arg( Settings::instance().maxFileShared() );
603 else if( bfsl->shareList().size() < 2 )
604 share_status = tr( "%1 is added to file sharing (%2)" ).arg( bfsl->folderPath(), Bee::bytesToString( bfsl->shareSize() ) );
605 else
606 share_status = tr( "%1 is added to file sharing with %2 files, %3" )
607 .arg( bfsl->folderPath() )
608 .arg( bfsl->shareList().size() )
609 .arg( Bee::bytesToString( bfsl->shareSize() ) );
610
611 dispatchSystemMessage( ID_DEFAULT_CHAT, ID_LOCAL_USER, QString( "%1 %2." ).arg( IconManager::instance().toHtml( "upload.png", "*F*" ), share_status ),
612 DispatchToChat, ChatMessage::System, false );
613
614 if( beeApp )
615 beeApp->removeJob( bfsl );
616
617 if( m_shareListToBuild == 0 )
618 {
619 #ifdef BEEBEEP_DEBUG
620 qDebug() << "Building local share list completed";
621 #endif
622 createLocalShareMessage();
623 emit localShareListAvailable();
624 }
625
626 bfsl->deleteLater();
627 }
628
removeAllPathsFromShare()629 void Core::removeAllPathsFromShare()
630 {
631 FileShare::instance().clearLocal();
632 QStringList local_share = Settings::instance().localShare();
633 local_share.clear();
634 Settings::instance().setLocalShare( local_share );
635 qDebug() << "All paths are removed from local shares";
636
637 QString share_status = tr( "All paths are removed from file sharing" );
638
639 dispatchSystemMessage( ID_DEFAULT_CHAT, ID_LOCAL_USER, QString( "%1 %2." ).arg( IconManager::instance().toHtml( "upload.png", "*F*" ), share_status ),
640 DispatchToChat, ChatMessage::FileTransfer, false );
641
642 createLocalShareMessage();
643 sendFileShareListToAll();
644
645 emit localShareListAvailable();
646 }
647
removePathFromShare(const QString & share_path)648 void Core::removePathFromShare( const QString& share_path )
649 {
650 int num_files = FileShare::instance().removePath( share_path );
651 QStringList local_share = Settings::instance().localShare();
652 if( local_share.removeOne( share_path ) )
653 Settings::instance().setLocalShare( local_share );
654 qDebug() << "Path" << share_path << "is removed from local shares";
655
656 QString share_status;
657 if( num_files < 2 )
658 share_status = tr( "%1 is removed from file sharing" ).arg( share_path );
659 else
660 share_status = tr( "%1 is removed from file sharing with %2 files" ).arg( share_path ).arg( num_files );
661
662 dispatchSystemMessage( ID_DEFAULT_CHAT, ID_LOCAL_USER, QString( "%1 %2." ).arg( IconManager::instance().toHtml( "upload.png", "*F*" ), share_status ),
663 DispatchToChat, ChatMessage::FileTransfer, false );
664
665 if( num_files > 0 )
666 {
667 createLocalShareMessage();
668 sendFileShareListToAll();
669 }
670
671 emit localShareListAvailable();
672 }
673
createLocalShareMessage()674 void Core::createLocalShareMessage()
675 {
676 if( mp_fileTransfer->isActive() )
677 Protocol::instance().createFileShareListMessage( FileShare::instance().local(), mp_fileTransfer->serverPort() );
678 else
679 Protocol::instance().createFileShareListMessage( FileShare::instance().local(), -1 );
680 }
681
buildLocalShareList()682 void Core::buildLocalShareList()
683 {
684 if( !FileShare::instance().local().isEmpty() )
685 {
686 FileShare::instance().clearLocal();
687 createLocalShareMessage();
688 sendFileShareListToAll();
689 }
690
691 if( !Settings::instance().enableFileTransfer() )
692 {
693 qWarning() << "Skip building local file share list: file transfer is disabled";
694 return;
695 }
696
697 if( !Settings::instance().enableFileSharing() )
698 {
699 qWarning() << "Skip building local file share list: file sharing is disabled";
700 return;
701 }
702
703 if( Settings::instance().localShare().isEmpty() )
704 {
705 qDebug() << "Skip building local file share list: there are not shared files";
706 return;
707 }
708
709 #ifdef BEEBEEP_DEBUG
710 qDebug() << "Building local file share list";
711 #endif
712
713 m_shareListToBuild = Settings::instance().localShare().size();
714
715 foreach( QString share_path, Settings::instance().localShare() )
716 addPathToShare( share_path );
717 }
718
sendFolder(const User & u,const QFileInfo & file_info,const QString & chat_private_id)719 bool Core::sendFolder( const User& u, const QFileInfo& file_info, const QString& chat_private_id )
720 {
721 QString icon_html = IconManager::instance().toHtml( "upload.png", "*F*" );
722
723 Chat chat_to_show_message = ChatManager::instance().findChatByPrivateId( chat_private_id, false, u.id() );
724
725 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), tr( "%1 You are about to send %2 to %3. Checking folder..." )
726 .arg( icon_html, file_info.fileName(), Bee::userNameToShow( u, true ) ),
727 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
728
729 BuildFileShareList *bfsl = new BuildFileShareList;
730 bfsl->setFolderPath( Bee::convertToNativeFolderSeparator( file_info.absoluteFilePath() ) );
731 bfsl->setUserId( u.id() );
732 bfsl->setChatPrivateId( chat_private_id );
733 connect( bfsl, SIGNAL( listCompleted() ), this, SLOT( addFolderToFileTransfer() ) );
734 if( beeApp )
735 beeApp->addJob( bfsl );
736 QMetaObject::invokeMethod( bfsl, "buildList", Qt::QueuedConnection );
737 return true;
738 }
739
addFolderToFileTransfer()740 void Core::addFolderToFileTransfer()
741 {
742 BuildFileShareList *bfsl = qobject_cast<BuildFileShareList*>( sender() );
743 if( !bfsl )
744 {
745 qWarning() << "Core received a signal from invalid BuildFileShareList instance";
746 return;
747 }
748
749 if( beeApp )
750 beeApp->removeJob( bfsl );
751 QString folder_name = bfsl->folderName();
752 QString folder_path = bfsl->folderPath();
753 QList<FileInfo> file_info_list = bfsl->shareList();
754 VNumber user_id = bfsl->userId();
755 QString chat_private_id = bfsl->chatPrivateId();
756 bfsl->deleteLater();
757
758 if( folder_name.isEmpty() )
759 {
760 qWarning() << "Unable to send folder. Internal error. Folder is empty";
761 return;
762 }
763
764 Chat chat_to_show_message = ChatManager::instance().findChatByPrivateId( chat_private_id, false, user_id );
765 User u = UserManager::instance().findUser( user_id );
766 QString sys_header = tr( "%1 Unable to send folder %2" ).arg( IconManager::instance().toHtml( "red-ball.png", "*F*" ) )
767 .arg( folder_name ) + QString( ": " );
768
769 if( !u.isValid() )
770 {
771 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, ID_LOCAL_USER, sys_header + tr( "invalid user #%1." ).arg( user_id ),
772 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
773 return;
774 }
775
776 if( file_info_list.isEmpty() )
777 {
778 if( Settings::instance().allowedFileExtensionsInFileTransfer().isEmpty() )
779 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, ID_LOCAL_USER, sys_header + tr( "the folder is empty." ),
780 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
781 else
782 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, ID_LOCAL_USER, sys_header + tr( "the folder is empty or contains only files that are not allowed." ),
783 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
784 return;
785 }
786
787 if( !mp_fileTransfer->isActive() )
788 {
789 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, ID_LOCAL_USER, sys_header + tr( "file transfer is not working." ),
790 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
791 return;
792 }
793
794 Connection* c = connection( u.id() );
795 if( !c )
796 {
797 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, ID_LOCAL_USER, sys_header + tr( "%1 is not connected." ).arg( Bee::userNameToShow( u, true ) ),
798 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
799 return;
800 }
801
802 qDebug() << "Sending folder" << folder_name << "to" << qPrintable( u.path() );
803
804 mp_fileTransfer->addFileInfoList( file_info_list );
805
806 QString icon_html = IconManager::instance().toHtml( "upload.png", "*F*" );
807 Message m = Protocol::instance().createFolderMessage( folder_name, file_info_list, mp_fileTransfer->serverPort() );
808
809 if( !m.isValid() )
810 {
811 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), sys_header + tr( "internal error." ),
812 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, false );
813 return;
814 }
815
816 QUrl folder_url = QUrl::fromLocalFile( folder_path );
817 QString msg = tr( "%1 Sending folder %2 to %3." ).arg( icon_html, QString( "<a href=\"%1\">%2</a>" ).arg( folder_url.toString(), folder_name ), Bee::userNameToShow( u, true ) );
818 dispatchSystemMessage( chat_to_show_message.isValid() ? chat_to_show_message.id() : ID_DEFAULT_CHAT, u.id(), msg,
819 chat_to_show_message.isValid() ? DispatchToChat : DispatchToDefaultAndPrivateChat, ChatMessage::FileTransfer, true );
820 c->sendMessage( m );
821 }
822
sendShareBoxRequest(VNumber user_id,const QString & folder_name,bool create_folder)823 void Core::sendShareBoxRequest( VNumber user_id, const QString& folder_name, bool create_folder )
824 {
825 if( user_id == ID_INVALID )
826 {
827 qWarning() << "Invalid user id found in Core::sendShareBoxRequest(...)";
828 }
829 else if( user_id != ID_LOCAL_USER )
830 {
831 #ifdef BEEBEEP_DEBUG
832 qDebug() << "BeeBOX sends request to user" << user_id << "for folder" << qPrintable( folder_name );
833 #endif
834 Message m = Protocol::instance().shareBoxRequestPathList( folder_name, create_folder );
835 Connection* c = connection( user_id );
836 if( c && c->sendMessage( m ) )
837 return;
838
839 User u = UserManager::instance().findUser( user_id );
840 qWarning() << "Unable to send share box request to offline user" << qPrintable( u.path() );
841 emit shareBoxUnavailable( u, folder_name );
842 }
843 else
844 {
845 if( Settings::instance().useShareBox() && !Settings::instance().shareBoxPath().isEmpty() )
846 buildShareBoxFileList( Settings::instance().localUser(), folder_name, create_folder );
847 else
848 emit shareBoxUnavailable( Settings::instance().localUser(), folder_name );
849 }
850 }
851
buildShareBoxFileList(const User & u,const QString & folder_name,bool create_folder)852 void Core::buildShareBoxFileList( const User& u, const QString& folder_name, bool create_folder )
853 {
854 #ifdef BEEBEEP_DEBUG
855 qDebug() << "BeeBOX builds file list in folder" << qPrintable( folder_name ) << "for user" << qPrintable( u.path() );
856 #endif
857 QString folder_path = QString( "%1%2%3" ).arg( Settings::instance().shareBoxPath() ).arg( QDir::separator() ).arg( folder_name );
858 if( create_folder )
859 {
860 QDir share_box_folder( Settings::instance().shareBoxPath() );
861 if( !share_box_folder.mkpath( folder_path ) )
862 {
863 qWarning() << "BeeBOX is unable to create folder" << qPrintable( folder_path ) << "for user" << qPrintable( u.path() );
864 if( u.isLocal() )
865 emit shareBoxUnavailable( u, folder_name );
866 else
867 sendMessageToLocalNetwork( u, Protocol::instance().refuseToShareBoxPath( folder_name, true ) );
868 return;
869 }
870 else
871 qDebug() << "BeeBOX creates folder" << qPrintable( folder_path ) << "for user" << qPrintable( u.path() );
872 }
873
874 BuildFileList *bfl = new BuildFileList;
875 bfl->init( folder_name, folder_path, u.id() );
876 connect( bfl, SIGNAL( listCompleted() ), this, SLOT( sendShareBoxList() ) );
877 if( beeApp )
878 beeApp->addJob( bfl );
879 QMetaObject::invokeMethod( bfl, "buildList", Qt::QueuedConnection );
880 }
881
sendShareBoxList()882 void Core::sendShareBoxList()
883 {
884 BuildFileList *bfl = qobject_cast<BuildFileList*>( sender() );
885 if( !bfl )
886 {
887 qWarning() << "Core received a signal from invalid BuildFileList instance";
888 return;
889 }
890
891 if( beeApp )
892 beeApp->removeJob( bfl );
893 QString folder_name = bfl->folderName();
894 VNumber to_user_id = bfl->toUserId();
895 bool error_found = bfl->errorFound();
896 QList<FileInfo> file_info_list = bfl->fileList();
897 bfl->deleteLater();
898
899 #ifdef BEEBEEP_DEBUG
900 qDebug() << "BeeBOX has found" << file_info_list.size() << "files in folder" << qPrintable( folder_name );
901 #endif
902
903 if( to_user_id != ID_LOCAL_USER )
904 {
905 if( !mp_fileTransfer->isActive() )
906 error_found = true;
907
908 Message m;
909 if( error_found )
910 m = Protocol::instance().refuseToShareBoxPath( folder_name, false );
911 else
912 m = Protocol::instance().acceptToShareBoxPath( folder_name, file_info_list, mp_fileTransfer->serverPort() );
913
914 Connection* c = connection( to_user_id );
915 if( c )
916 c->sendMessage( m );
917 #ifdef BEEBEEP_DEBUG
918 else
919 qWarning() << "Unable to find connection for user" << to_user_id << "to send share box message";
920 #endif
921 }
922 else
923 emit shareBoxAvailable( Settings::instance().localUser(), folder_name, file_info_list );
924 }
925
downloadFromShareBox(VNumber from_user_id,const FileInfo & fi,const QString & to_path)926 void Core::downloadFromShareBox( VNumber from_user_id, const FileInfo& fi, const QString& to_path )
927 {
928 #ifdef BEEBEEP_DEBUG
929 QString from_path = fi.shareFolder().isEmpty() ? fi.name() : QString( "%1/%2" ).arg( fi.shareFolder(), fi.name() );
930 qDebug() << "Download path" << from_path << "from user" << from_user_id << "to path" << to_path
931 << "from server" << qPrintable( fi.networkAddress().toString() );
932 #else
933 Q_UNUSED( from_user_id );
934 #endif
935
936 FileInfo download_file_info = fi;
937 download_file_info.setPath( to_path );
938 mp_fileTransfer->downloadFile( from_user_id, download_file_info );
939 }
940
uploadToShareBox(VNumber to_user_id,const FileInfo & fi,const QString & to_path)941 void Core::uploadToShareBox( VNumber to_user_id, const FileInfo& fi, const QString& to_path )
942 {
943 #ifdef BEEBEEP_DEBUG
944 qDebug() << "Upload file" << fi.path() << "to path" << to_path << "of user" << to_user_id;
945 #endif
946 sendFile( to_user_id, fi.path(), to_path, true, ID_INVALID );
947 }
948
resumeFileTransfer(VNumber user_id,const FileInfo & file_info)949 bool Core::resumeFileTransfer( VNumber user_id, const FileInfo& file_info )
950 {
951 if( file_info.isDownload() )
952 {
953 #ifdef BEEBEEP_DEBUG
954 qDebug() << "Try to resume downloading file" << qPrintable( file_info.path() ) << "from user" << user_id;
955 #endif
956 mp_fileTransfer->downloadFile( user_id, file_info );
957 }
958 else
959 {
960 #ifdef BEEBEEP_DEBUG
961 qDebug() << "Try to resume uploading file" << qPrintable( file_info.path() ) << "to user" << user_id;
962 #endif
963 Chat c = ChatManager::instance().findChatByPrivateId( file_info.chatPrivateId(), false, user_id );
964 return sendFile( user_id, file_info.path(), file_info.shareFolder(), file_info.isInShareBox(), c.id() );
965 }
966 return false;
967 }
968