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: CoreConnection.cpp 1459 2020-12-29 10:46:47Z mastroddi $
21 //
22 //////////////////////////////////////////////////////////////////////
23 
24 #include "Avatar.h"
25 #include "BeeUtils.h"
26 #include "Broadcaster.h"
27 #include "ChatManager.h"
28 #include "ColorManager.h"
29 #include "Connection.h"
30 #include "Core.h"
31 #include "FileShare.h"
32 #include "FirewallManager.h"
33 #include "Hive.h"
34 #include "IconManager.h"
35 #include "NetworkManager.h"
36 #include "Protocol.h"
37 #include "Settings.h"
38 #include "UserManager.h"
39 #ifdef BEEBEEP_USE_SHAREDESKTOP
40   #include "ShareDesktop.h"
41 #endif
42 
43 
connection(VNumber user_id) const44 Connection* Core::connection( VNumber user_id ) const
45 {
46   foreach( Connection* c, m_connections )
47   {
48     if( c->userId() == user_id )
49       return c;
50   }
51   return Q_NULLPTR;
52 }
53 
hasConnection(const QHostAddress & sender_ip,int sender_port) const54 bool Core::hasConnection( const QHostAddress& sender_ip, int sender_port ) const
55 {
56   foreach( Connection* c, m_connections )
57   {
58     if( (sender_port == -1 || c->peerPort() == sender_port) && c->peerAddress() == sender_ip )
59     {
60       if( c->isConnected() || c->isConnecting() )
61       {
62 #ifdef BEEBEEP_DEBUG
63         qDebug() << "Connection from" << qPrintable( sender_ip.toString() ) << sender_port << "is already opened";
64 #endif
65         return true;
66       }
67     }
68   }
69 
70   return false;
71 }
72 
checkNetworkAddress(const NetworkAddress & na)73 void Core::checkNetworkAddress( const NetworkAddress& na )
74 {
75   if( na == Settings::instance().localUser().networkAddress() )
76     return;
77 
78   newPeerFound( na.hostAddress(), na.hostPort() );
79 }
80 
createConnection()81 Connection* Core::createConnection()
82 {
83   Connection *c = new Connection( this );
84   m_connections.append( c );
85   return c;
86 }
87 
newPeerFound(const QHostAddress & sender_ip,int sender_port)88 void Core::newPeerFound( const QHostAddress& sender_ip, int sender_port )
89 {
90   if( !isConnected() )
91     return;
92 
93   if( NetworkAddress::isLoopback( sender_ip ) && sender_port == mp_listener->serverPort() )
94     return;
95 
96   if( NetworkManager::instance().isLocalHostAddress( sender_ip ) && sender_port == mp_listener->serverPort() )
97   {
98 #ifdef BEEBEEP_DEBUG
99     qDebug() << "Skip new peer found" << qPrintable( sender_ip.toString() ) << ":" << sender_port << "because it is local user";
100 #endif
101     return;
102   }
103 
104   if( !NetworkManager::instance().isHostAddressAllowed( sender_ip ) )
105   {
106     qWarning() << "New peer found" << qPrintable( sender_ip.toString() ) << "is not allowed by file HOSTS";
107     return;
108   }
109 
110   if( Settings::instance().preventMultipleConnectionsFromSingleHostAddress() )
111   {
112     if( hasConnection( sender_ip, -1 ) )
113     {
114       qDebug() << "New peer found" << qPrintable( sender_ip.toString() ) << "is already connected and blocked by prevent multiple connections option";
115       return;
116     }
117   }
118 
119   NetworkAddress na( sender_ip, static_cast<quint16>( sender_port ) );
120   if( isUserConnected( na ) )
121   {
122     User u = UserManager::instance().findUserByNetworkAddress( na );
123     if( u.isValid() )
124       qDebug() << "Skip new peer found" << qPrintable( sender_ip.toString() ) << "from connected user" << qPrintable( u.name() );
125     else
126       qWarning() << "New peer found" << qPrintable( sender_ip.toString() ) << "is already connected (but user is not authorized yet)";
127     return;
128   }
129 
130   qDebug() << "Connecting to new peer found" << qPrintable( sender_ip.toString() ) << sender_port;
131 
132   Connection *c = createConnection();
133   setupNewConnection( c );
134   c->connectToNetworkAddress( na );
135 }
136 
checkNewConnection(qintptr socket_descriptor)137 void Core::checkNewConnection( qintptr socket_descriptor )
138 {
139   Connection *c = createConnection();
140   c->initSocket( socket_descriptor, mp_listener->serverPort() );
141   qDebug() << "New connection to port" << mp_listener->serverPort() << "from" << qPrintable( c->networkAddress().toString() );
142   if( NetworkManager::instance().isHostAddressAllowed( c->networkAddress().hostAddress() ) )
143   {
144     qDebug() << "New connection to port" << mp_listener->serverPort() << "from" << qPrintable( c->networkAddress().toString() );
145     setupNewConnection( c );
146   }
147   else
148   {
149     qWarning() << "New connection to port" << mp_listener->serverPort() << "from" << qPrintable( c->networkAddress().toString() ) << "is not allowed by file HOSTS";
150     closeConnection( c );
151   }
152 }
153 
setupNewConnection(Connection * c)154 void Core::setupNewConnection( Connection *c )
155 {
156 #ifdef BEEBEEP_DEBUG
157   if( !c->peerAddress().isNull() )
158     qDebug() << "Connecting SIGNAL/SLOT to connection from" << qPrintable( c->networkAddress().toString() );
159 #endif
160   connect( c, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( setConnectionError( QAbstractSocket::SocketError ) ) );
161   connect( c, SIGNAL( disconnected() ), this, SLOT( setConnectionClosed() ) );
162   connect( c, SIGNAL( abortRequest() ), this, SLOT( setConnectionClosed() ) );
163   connect( c, SIGNAL( authenticationRequested( const QByteArray& ) ), this, SLOT( checkUserAuthentication( const QByteArray& ) ) );
164 }
165 
addConnectionReadyForUse(Connection * c,const User & u)166 void Core::addConnectionReadyForUse( Connection* c, const User& u )
167 {
168 #ifdef BEEBEEP_DEBUG
169   qDebug() << "Connection from" << qPrintable( c->networkAddress().toString() ) << "is ready for use by" << qPrintable( u.path() );
170 #endif
171   c->setReadyForUse( u.id() );
172   connect( c, SIGNAL( newMessage( VNumber, const Message& ) ), this, SLOT( parseMessage( VNumber, const Message& ) ) );
173 }
174 
setConnectionError(QAbstractSocket::SocketError se)175 void Core::setConnectionError( QAbstractSocket::SocketError se )
176 {
177   Connection* c = qobject_cast<Connection*>( sender() );
178   if( c )
179   {
180     qWarning() << "Connection from" << qPrintable( c->networkAddress().toString() ) << "has an error:" << c->errorString() << "-" << static_cast<int>(se);
181     closeConnection( c );
182   }
183   else
184     qWarning() << "Connection error" << se << "occurred but the object caller is invalid";
185 }
186 
setConnectionClosed()187 void Core::setConnectionClosed()
188 {
189   Connection* c = qobject_cast<Connection*>( sender() );
190   if( c )
191     closeConnection( c );
192   else
193     qWarning() << "Connection closed but the object caller is invalid";
194 }
195 
closeConnection(Connection * c)196 void Core::closeConnection( Connection *c )
197 {
198   if( !m_connections.removeOne( c ) )
199     return;
200 
201   if( c->userId() != ID_INVALID )
202   {
203     User u = UserManager::instance().findUser( c->userId() );
204     if( u.isValid() )
205     {
206       qDebug() << "User" << qPrintable( u.path() ) << "goes offline";
207       u.setStatus( User::Offline );
208       u.setLastConnection( QDateTime::currentDateTime() );
209       UserManager::instance().setUser( u );
210       UserManager::instance().removeNewConnectedUserId( u.id() );
211 
212       emit userChanged( u );
213       emit userConnectionStatusChanged( u );
214 
215       Chat default_chat = ChatManager::instance().defaultChat();
216       if( default_chat.removeUser( u.id() ) )
217         ChatManager::instance().setChat( default_chat );
218 
219       FileShare::instance().removeFromNetwork( u.id() );
220       emit fileShareAvailable( u );
221 
222       if( isConnected() )
223         mp_fileTransfer->removeFilesToUser( u.id() );
224 
225 #ifdef BEEBEEP_USE_SHAREDESKTOP
226       if( mp_shareDesktop->isActive() )
227         stopShareDesktop( u.id() );
228 #endif
229     }
230     else
231       qWarning() << "User" << c->userId() << "not found while closing connection";
232   }
233 
234 #ifdef BEEBEEP_DEBUG
235   qDebug() << "Deleting connection" << qPrintable( c->networkAddress().toString() );
236 #endif
237   c->disconnect();
238   c->abortConnection();
239   c->deleteLater();
240 }
241 
checkUserAuthentication(const QByteArray & auth_byte_array)242 void Core::checkUserAuthentication( const QByteArray& auth_byte_array )
243 {
244   Connection* c = qobject_cast<Connection*>( sender() );
245   if( !c )
246   {
247     qWarning() << "Unable to check authentication for an invalid connection object";
248     return;
249   }
250 
251   Message m = Protocol::instance().toMessage( auth_byte_array, c->protocolVersion() );
252   if( !m.isValid() )
253   {
254     qWarning() << "Core has received an invalid HELLO from" << qPrintable( c->networkAddress().toString() );
255     closeConnection( c );
256     return;
257   }
258 
259   if( m.type() != Message::Hello )
260   {
261     qWarning() << "Core is waiting for HELLO, but another message type" << m.type() << "is arrived from" << qPrintable( c->networkAddress().toString() );
262     closeConnection( c );
263     return;
264   }
265 
266   User u = Protocol::instance().createUser( m, c->peerAddress() );
267   if( !u.isValid() )
268   {
269     qWarning() << "Unable to create a new user (invalid protocol or password) from the message arrived from:" << qPrintable( c->networkAddress().toString() );
270     closeConnection( c );
271     return;
272   }
273   else
274     qDebug() << qPrintable( u.path() ) << "has completed the authentication from peer" << qPrintable( c->networkAddress().toString() );
275 
276   User user_found = Protocol::instance().recognizeUser( u, Settings::instance().userRecognitionMethod() );
277   bool user_path_changed = false;
278 
279   if( user_found.isValid() )
280   {
281     QString sAlertMsg;
282     user_path_changed = (u.path() != user_found.path());
283     if( user_found.isLocal() )
284     {
285       if( user_path_changed )
286       {
287  #ifdef BEEBEEP_DEBUG
288         qDebug() << "User path changed:" << u.path() << "vs" << user_found.path();
289  #endif
290         if( Settings::instance().userRecognitionMethod() == Settings::RecognizeByAccountAndDomain )
291         {
292           sAlertMsg = tr( "%1 Connection closed to user %2 because it uses your account name: %3." )
293                           .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ), u.accountPath() );
294           sAlertMsg += QString( " (%1)" ).arg( tr( "domain" ) );
295         }
296         else if( Settings::instance().userRecognitionMethod() == Settings::RecognizeByAccount )
297         {
298           sAlertMsg = tr( "%1 Connection closed to user %2 because it uses your account name: %3." )
299                           .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ), u.accountName() );
300         }
301         else
302         {
303           if( u.name() == user_found.name() )
304           {
305             sAlertMsg = tr( "%1 Connection closed to user %2 because it uses your nickname: %3." )
306                           .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ), Bee::replaceHtmlSpecialCharacters( u.name() ) );
307           }
308           else
309           {
310             sAlertMsg = tr( "%1 Connection closed to user %2 because it uses your hash code." )
311                           .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ) );
312           }
313         }
314         dispatchSystemMessage( ID_DEFAULT_CHAT, user_found.id(), sAlertMsg, DispatchToDefaultAndPrivateChat, ChatMessage::Connection, false );
315         qWarning() << "User with account" << qPrintable( u.accountPath() ) << "and path" << qPrintable( u.path() ) << "is recognized to be Local";
316       }
317       else
318         qDebug() << "Test connection from localhost to port" << mp_listener->serverPort() << "was successful";
319 
320       closeConnection( c );
321       return;
322     }
323 
324     if( isUserConnected( user_found.id() ) )
325     {
326       if( user_path_changed )
327       {
328         if( Settings::instance().userRecognitionMethod() == Settings::RecognizeByAccountAndDomain )
329         {
330           sAlertMsg = tr( "%1 Connection closed to user %2 because it uses same account name of the already connected user %3: %4." )
331                         .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ), Bee::replaceHtmlSpecialCharacters( user_found.path() ), u.accountPath() );
332           sAlertMsg += QString( " (%1)" ).arg( tr( "domain" ) );
333           qDebug() << "User" << qPrintable( u.path() ) << "is already connected with account (domain)" << qPrintable( user_found.accountPath() );
334         }
335         else if( Settings::instance().userRecognitionMethod() == Settings::RecognizeByAccount )
336         {
337           sAlertMsg = tr( "%1 Connection closed to user %2 because it uses same account name of the already connected user %3: %4." )
338                         .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ), Bee::replaceHtmlSpecialCharacters( user_found.path() ), u.accountName() );
339           qDebug() << "User" << qPrintable( u.path() ) << "is already connected with account" << qPrintable( user_found.accountPath() );
340         }
341         else
342         {
343           if( u.name() == user_found.name() )
344           {
345             sAlertMsg = tr( "%1 Connection closed to user %2 because it uses same nickname of the already connected user %3: %4." )
346                           .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ), Bee::replaceHtmlSpecialCharacters( user_found.path() ), Bee::userNameToShow( u, true ) );
347             qDebug() << "User" << qPrintable( u.path() ) << "is already connected with the same nickname of" << qPrintable( user_found.path() );
348           }
349           else
350           {
351             sAlertMsg = tr( "%1 Connection closed to user %2 because it uses same hash code of the already connected user %3: %4." )
352                           .arg( IconManager::instance().toHtml( "warning.png", "*E*" ), Bee::replaceHtmlSpecialCharacters( u.path() ), Bee::replaceHtmlSpecialCharacters( user_found.path() ), Bee::userNameToShow( u, true ) );
353             qDebug() << "User" << qPrintable( u.path() ) << "is already connected with the same hash code of" << qPrintable( user_found.path() );
354           }
355         }
356         dispatchSystemMessage( ID_DEFAULT_CHAT, user_found.id(), sAlertMsg, DispatchToDefaultAndPrivateChat, ChatMessage::Connection, false );
357       }
358       else
359       {
360         qDebug() << "User" << qPrintable( u.path() ) << "is already online with another connection:" << qPrintable( user_found.path() );
361         int remote_protocol_version = c->protocolVersion();
362         closeConnection( c );
363         if( remote_protocol_version < Settings::instance().protocolVersion() ||
364           ( remote_protocol_version == Settings::instance().protocolVersion() && user_found.path() < Settings::instance().localUser().path() ) ) // just one of them to prevent collision
365         {
366           if( mp_broadcaster->addNetworkAddress( user_found.networkAddress() ) )
367             qDebug() << "User" << qPrintable( user_found.path() ) << "is added to addresses to be contacted later";
368         }
369       }
370       return;
371     }
372 
373     if( user_path_changed )
374       qDebug() << "On connection old user found" << qPrintable( user_found.path() ) << "and associated to" << qPrintable( u.path() );
375 
376     u.setId( user_found.id() );
377     u.setIsFavorite( user_found.isFavorite() );
378     if( !ColorManager::instance().isValidColor( u.color() ) || u.color() == QString( "#000000" ) )
379       u.setColor( user_found.color() );
380   }
381   else
382     qDebug() << "New user connected:" << qPrintable( u.path() );
383 
384   if( !ColorManager::instance().isValidColor( u.color() ) || u.color() == QString( "#000000" ) )
385     u.setColor( ColorManager::instance().unselectedQString() );
386   u.setProtocolVersion( c->protocolVersion() );
387   u.setLastConnection( QDateTime::currentDateTime() );
388   UserManager::instance().setUser( u );
389 
390 #ifdef BEEBEEP_DEBUG
391   qDebug() << "User" << qPrintable( u.path() ) << "added with id" << u.id() << "and color" << qPrintable( u.color() );
392 #endif
393 
394   Chat default_chat = ChatManager::instance().defaultChat();
395   if( default_chat.addUser( u.id() ) )
396     ChatManager::instance().setChat( default_chat );
397 
398   Chat private_chat = ChatManager::instance().privateChatForUser( u.id() );
399   if( private_chat.isValid() )
400   {
401     if( user_found.isValid() && u.name() != user_found.name() )
402     {
403       ChatManager::instance().changePrivateChatNameAfterUserNameChanged( u.id(), u.name() );
404       showUserNameChanged( u, user_found.name() );
405     }
406   }
407   else
408     createPrivateChat( u );
409 
410   addConnectionReadyForUse( c, u );
411 
412   emit userChanged( u );
413   emit userConnectionStatusChanged( u );
414   showMessage( tr( "%1 users connected" ).arg( connectedUsers() ), 3000 );
415 
416   if( c->isEncrypted() )
417   {
418     if( c->protocolVersion() < SECURE_LEVEL_3_PROTO_VERSION )
419     {
420       dispatchSystemMessage( ID_DEFAULT_CHAT, u.id(),
421                              tr( "%1 %2 uses old encryption level." ).arg( IconManager::instance().toHtml( "warning.png", "*!*" ), Bee::userNameToShow( u, true ) ),
422                              DispatchToAllChatsWithUser, ChatMessage::Connection, false );
423     }
424 
425     if( Settings::instance().disableConnectionSocketEncryption() )
426     {
427       dispatchSystemMessage( ID_DEFAULT_CHAT, u.id(),
428                              QString( "%1 %2." ).arg( IconManager::instance().toHtml( "encryption-enabled.png", "*!*" ),
429                                                       tr( "%1 has end-to-end encryption enabled" ).arg( Bee::userNameToShow( u, true ) ) ),
430                              DispatchToAllChatsWithUser, ChatMessage::Connection, false );
431     }
432   }
433   else
434   {
435     if( !Settings::instance().disableConnectionSocketEncryption() )
436     {
437       dispatchSystemMessage( ID_DEFAULT_CHAT, u.id(),
438                              QString( "%1 %2." ).arg( IconManager::instance().toHtml( "encryption-disabled.png", "*!*" ),
439                                                       tr( "%1 has end-to-end encryption disabled" ).arg( Bee::userNameToShow( u, true ) ) ),
440                              DispatchToAllChatsWithUser, ChatMessage::Connection, false );
441     }
442   }
443 
444   if( !Settings::instance().localUser().vCard().hasOnlyNickName() )
445   {
446     if( c->protocolVersion() > 1 )
447     {
448 #ifdef BEEBEEP_DEBUG
449       qDebug() << "Sending my VCard to" << qPrintable( u.path() );
450 #endif
451       c->sendMessage( Protocol::instance().localVCardMessage() );
452     }
453   }
454 
455   UserManager::instance().addNewConnectedUserId( u.id() );
456 }
457 
connectedUsers() const458 int Core::connectedUsers() const
459 {
460   int connected_users = 0;
461   if( m_connections.isEmpty() )
462     return connected_users;
463 
464   foreach( Connection* c, m_connections )
465   {
466     if( c->isConnected() && c->isReadyForUse() )
467       connected_users++;
468   }
469 
470   return connected_users;
471 }
472 
checkConnectionPorts()473 void Core::checkConnectionPorts()
474 {
475   if( isConnected() && connectedUsers() == 0 )
476   {
477     // FIXME: it works always... not useful to check if firewall ports are opened
478     NetworkAddress na( NetworkManager::instance().localHostAddress(), mp_listener->serverPort() );
479     qDebug() << "Checking connection to localhost" << qPrintable( na.toString() );
480     Connection *c = createConnection();
481     setupNewConnection( c );
482     c->connectToNetworkAddress( na );
483   }
484 }
485 
checkFirewall()486 void Core::checkFirewall()
487 {
488   // FIXME: how to do this?
489   FirewallManager::instance().allowApplication( Settings::instance().programName(), QDir::toNativeSeparators( qApp->applicationFilePath() ) );
490 }
491 
updateNetworkConfiguration(const QNetworkConfiguration & net_conf)492 void Core::updateNetworkConfiguration( const QNetworkConfiguration& net_conf )
493 {
494   if( !isConnected() )
495   {
496     qDebug() << "Core is not connected and skips to check network configuration:" << qPrintable( net_conf.name() ) << "-" << qPrintable( net_conf.identifier() ) << "-" << qPrintable( net_conf.bearerTypeName() );;
497     return;
498   }
499 
500 #ifdef BEEBEEP_DEBUG
501   qDebug() << "Core is checking network configuration:" << qPrintable( net_conf.name() ) << "-" << qPrintable( net_conf.identifier() )
502            << "- bearer:" << qPrintable( net_conf.bearerTypeName() ) << net_conf.bearerType()
503 #if QT_VERSION > 0x050000
504            << net_conf.bearerTypeFamily()
505 #endif
506            << "- purpose:" << net_conf.purpose()
507            << "- type:" << net_conf.type()
508            << "- state:" << net_conf.state();
509 #endif
510 
511   if( net_conf.state() == QNetworkConfiguration::Active && (net_conf.bearerType() == QNetworkConfiguration::BearerEthernet || net_conf.bearerType() == QNetworkConfiguration::BearerWLAN) )
512   {
513     qDebug() << "Network configuration:" << qPrintable( net_conf.name() ) << "-" << qPrintable( net_conf.identifier() ) << "is active ethernet or wlan and new broadcast will be requested";
514     mp_broadcaster->sendBroadcast();
515   }
516 }
517