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