File indexing completed on 2024-05-12 05:52:02

0001 // The MIT License (MIT)
0002 //
0003 // Copyright (c) Itay Grudev 2015 - 2020
0004 //
0005 // Permission is hereby granted, free of charge, to any person obtaining a copy
0006 // of this software and associated documentation files (the "Software"), to deal
0007 // in the Software without restriction, including without limitation the rights
0008 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0009 // copies of the Software, and to permit persons to whom the Software is
0010 // furnished to do so, subject to the following conditions:
0011 //
0012 // The above copyright notice and this permission notice shall be included in
0013 // all copies or substantial portions of the Software.
0014 //
0015 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0018 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0020 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0021 // THE SOFTWARE.
0022 
0023 //
0024 //  W A R N I N G !!!
0025 //  -----------------
0026 //
0027 // This file is not part of the SingleApplication API. It is used purely as an
0028 // implementation detail. This header file may change from version to
0029 // version without notice, or may even be removed.
0030 //
0031 
0032 #include <cstdlib>
0033 #include <cstddef>
0034 
0035 #include <QtCore/QThread>
0036 #include <QtCore/QByteArray>
0037 #include <QtCore/QDataStream>
0038 #include <QtCore/QElapsedTimer>
0039 #include <QtCore/QCryptographicHash>
0040 
0041 #include <QtCore/QRandomGenerator>
0042 
0043 #include "singleapplication_p.h"
0044 
0045 #ifdef Q_OS_UNIX
0046     #include <unistd.h>
0047     #include <sys/types.h>
0048     #include <pwd.h>
0049 #endif
0050 
0051 #ifdef Q_OS_WIN
0052     #ifndef NOMINMAX
0053         #define NOMINMAX 1
0054     #endif
0055     #include <windows.h>
0056     #include <lmcons.h>
0057 #endif
0058 
0059 SingleApplicationPrivate::SingleApplicationPrivate( SingleApplication *q_ptr )
0060     : q_ptr( q_ptr )
0061 {
0062     server = nullptr;
0063     socket = nullptr;
0064     memory = nullptr;
0065     instanceNumber = 0;
0066 }
0067 
0068 SingleApplicationPrivate::~SingleApplicationPrivate()
0069 {
0070     if( socket != nullptr ){
0071         socket->close();
0072         delete socket;
0073     }
0074 
0075     if( memory != nullptr ){
0076         memory->lock();
0077         auto *inst = static_cast<InstancesInfo*>(memory->data());
0078         if( server != nullptr ){
0079             server->close();
0080             delete server;
0081             inst->primary = false;
0082             inst->primaryPid = -1;
0083             inst->primaryUser[0] =  '\0';
0084             inst->checksum = blockChecksum();
0085         }
0086         memory->unlock();
0087 
0088         delete memory;
0089     }
0090 }
0091 
0092 QString SingleApplicationPrivate::getUsername()
0093 {
0094 #ifdef Q_OS_WIN
0095       wchar_t username[UNLEN + 1];
0096       // Specifies size of the buffer on input
0097       DWORD usernameLength = UNLEN + 1;
0098       if( GetUserNameW( username, &usernameLength ) )
0099           return QString::fromWCharArray( username );
0100       return qEnvironmentVariable( "USERNAME" );
0101 #endif
0102 #ifdef Q_OS_UNIX
0103       QString username;
0104       uid_t uid = geteuid();
0105       struct passwd *pw = getpwuid( uid );
0106       if( pw )
0107           username = QString::fromLocal8Bit( pw->pw_name );
0108       if ( username.isEmpty() ){
0109           username = qEnvironmentVariable( "USER" );
0110       }
0111       return username;
0112 #endif
0113 }
0114 
0115 void SingleApplicationPrivate::genBlockServerName()
0116 {
0117     QCryptographicHash appData( QCryptographicHash::Sha256 );
0118 #if QT_VERSION < QT_VERSION_CHECK(6, 3, 0)
0119     appData.addData( "SingleApplication", 17 );
0120 #else
0121     appData.addData( QByteArrayView{"SingleApplication"} );    
0122 #endif
0123     appData.addData( SingleApplication::app_t::applicationName().toUtf8() );
0124     appData.addData( SingleApplication::app_t::organizationName().toUtf8() );
0125     appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() );
0126 
0127     if ( ! appDataList.isEmpty() )
0128         appData.addData( appDataList.join(QString()).toUtf8() );
0129 
0130     if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ){
0131         appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() );
0132     }
0133 
0134     if( ! (options & SingleApplication::Mode::ExcludeAppPath) ){
0135 #if defined(Q_OS_WIN)
0136         appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() );
0137 #elif defined(Q_OS_LINUX)
0138         // If the application is running as an AppImage then the APPIMAGE env var should be used
0139         // instead of applicationPath() as each instance is launched with its own executable path
0140         const QByteArray appImagePath = qgetenv( "APPIMAGE" );
0141         if( appImagePath.isEmpty() ){ // Not running as AppImage: use path to executable file
0142             appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
0143         } else { // Running as AppImage: Use absolute path to AppImage file
0144             appData.addData( appImagePath );
0145         };
0146 #else
0147         appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() );
0148 #endif
0149     }
0150 
0151     // User level block requires a user specific data in the hash
0152     if( options & SingleApplication::Mode::User ){
0153         appData.addData( getUsername().toUtf8() );
0154     }
0155 
0156     // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with
0157     // server naming requirements.
0158     blockServerName = QString::fromUtf8(appData.result().toBase64().replace("/", "_"));
0159 }
0160 
0161 void SingleApplicationPrivate::initializeMemoryBlock() const
0162 {
0163     auto *inst = static_cast<InstancesInfo*>( memory->data() );
0164     inst->primary = false;
0165     inst->secondary = 0;
0166     inst->primaryPid = -1;
0167     inst->primaryUser[0] =  '\0';
0168     inst->checksum = blockChecksum();
0169 }
0170 
0171 void SingleApplicationPrivate::startPrimary()
0172 {
0173     // Reset the number of connections
0174     auto *inst = static_cast <InstancesInfo*>( memory->data() );
0175 
0176     inst->primary = true;
0177     inst->primaryPid = QCoreApplication::applicationPid();
0178     qstrncpy( inst->primaryUser, getUsername().toUtf8().data(), sizeof(inst->primaryUser) );
0179     inst->checksum = blockChecksum();
0180     instanceNumber = 0;
0181     // Successful creation means that no main process exists
0182     // So we start a QLocalServer to listen for connections
0183     QLocalServer::removeServer( blockServerName );
0184     server = new QLocalServer();
0185 
0186     // Restrict access to the socket according to the
0187     // SingleApplication::Mode::User flag on User level or no restrictions
0188     if( options & SingleApplication::Mode::User ){
0189         server->setSocketOptions( QLocalServer::UserAccessOption );
0190     } else {
0191         server->setSocketOptions( QLocalServer::WorldAccessOption );
0192     }
0193 
0194     server->listen( blockServerName );
0195     QObject::connect(
0196         server,
0197         &QLocalServer::newConnection,
0198         this,
0199         &SingleApplicationPrivate::slotConnectionEstablished
0200     );
0201 }
0202 
0203 void SingleApplicationPrivate::startSecondary()
0204 {
0205   auto *inst = static_cast <InstancesInfo*>( memory->data() );
0206 
0207   inst->secondary += 1;
0208   inst->checksum = blockChecksum();
0209   instanceNumber = inst->secondary;
0210 }
0211 
0212 bool SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType connectionType )
0213 {
0214     QElapsedTimer time;
0215     time.start();
0216 
0217     // Connect to the Local Server of the Primary Instance if not already
0218     // connected.
0219     if( socket == nullptr ){
0220         socket = new QLocalSocket();
0221     }
0222 
0223     if( socket->state() == QLocalSocket::ConnectedState ) return true;
0224 
0225     if( socket->state() != QLocalSocket::ConnectedState ){
0226 
0227         while( true ){
0228             randomSleep();
0229 
0230           if( socket->state() != QLocalSocket::ConnectingState )
0231             socket->connectToServer( blockServerName );
0232 
0233           if( socket->state() == QLocalSocket::ConnectingState ){
0234               socket->waitForConnected( static_cast<int>(msecs - time.elapsed()) );
0235           }
0236 
0237           // If connected break out of the loop
0238           if( socket->state() == QLocalSocket::ConnectedState ) break;
0239 
0240           // If elapsed time since start is longer than the method timeout return
0241           if( time.elapsed() >= msecs ) return false;
0242         }
0243     }
0244 
0245     // Initialisation message according to the SingleApplication protocol
0246     QByteArray initMsg;
0247     QDataStream writeStream(&initMsg, QIODevice::WriteOnly);
0248 
0249     writeStream.setVersion(QDataStream::Qt_5_6);
0250 
0251     writeStream << blockServerName.toLatin1();
0252     writeStream << static_cast<quint8>(connectionType);
0253     writeStream << instanceNumber;
0254     quint16 checksum = qChecksum(QByteArray(initMsg.constData(), static_cast<quint32>(initMsg.length())));
0255     writeStream << checksum;
0256 
0257     return writeConfirmedMessage( static_cast<int>(msecs - time.elapsed()), initMsg );
0258 }
0259 
0260 void SingleApplicationPrivate::writeAck( QLocalSocket *sock ) {
0261     sock->putChar('\n');
0262 }
0263 
0264 bool SingleApplicationPrivate::writeConfirmedMessage (int msecs, const QByteArray &msg, SingleApplication::SendMode sendMode)
0265 {
0266     QElapsedTimer time;
0267     time.start();
0268 
0269     // Frame 1: The header indicates the message length that follows
0270     QByteArray header;
0271     QDataStream headerStream(&header, QIODevice::WriteOnly);
0272 
0273     headerStream.setVersion(QDataStream::Qt_5_6);
0274     headerStream << static_cast <quint64>( msg.length() );
0275 
0276     if( ! writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), header ))
0277         return false;
0278 
0279     // Frame 2: The message
0280     const bool result = writeConfirmedFrame( static_cast<int>(msecs - time.elapsed()), msg );
0281 
0282     // Block if needed
0283     if (socket && sendMode == SingleApplication::BlockUntilPrimaryExit)
0284         socket->waitForDisconnected(-1);
0285 
0286     return result;
0287 }
0288 
0289 bool SingleApplicationPrivate::writeConfirmedFrame( int msecs, const QByteArray &msg )
0290 {
0291     socket->write( msg );
0292     socket->flush();
0293 
0294     bool result = socket->waitForReadyRead( msecs ); // await ack byte
0295     if (result) {
0296         socket->read( 1 );
0297         return true;
0298     }
0299 
0300     return false;
0301 }
0302 
0303 quint16 SingleApplicationPrivate::blockChecksum() const
0304 {
0305     quint16 checksum = qChecksum(QByteArray(static_cast<const char*>(memory->constData()), offsetof(InstancesInfo, checksum)));
0306     return checksum;
0307 }
0308 
0309 qint64 SingleApplicationPrivate::primaryPid() const
0310 {
0311     qint64 pid;
0312 
0313     memory->lock();
0314     auto *inst = static_cast<InstancesInfo*>( memory->data() );
0315     pid = inst->primaryPid;
0316     memory->unlock();
0317 
0318     return pid;
0319 }
0320 
0321 QString SingleApplicationPrivate::primaryUser() const
0322 {
0323     QByteArray username;
0324 
0325     memory->lock();
0326     auto *inst = static_cast<InstancesInfo*>( memory->data() );
0327     username = inst->primaryUser;
0328     memory->unlock();
0329 
0330     return QString::fromUtf8( username );
0331 }
0332 
0333 /**
0334  * @brief Executed when a connection has been made to the LocalServer
0335  */
0336 void SingleApplicationPrivate::slotConnectionEstablished()
0337 {
0338     QLocalSocket *nextConnSocket = server->nextPendingConnection();
0339     connectionMap.insert(nextConnSocket, ConnectionInfo());
0340 
0341     QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, this,
0342         [nextConnSocket, this](){
0343             auto &info = connectionMap[nextConnSocket];
0344             this->slotClientConnectionClosed( nextConnSocket, info.instanceId );
0345         }
0346     );
0347 
0348     QObject::connect(nextConnSocket, &QLocalSocket::disconnected, nextConnSocket, &QLocalSocket::deleteLater);
0349 
0350     QObject::connect(nextConnSocket, &QLocalSocket::destroyed, this,
0351         [nextConnSocket, this](){
0352             connectionMap.remove(nextConnSocket);
0353         }
0354     );
0355 
0356     QObject::connect(nextConnSocket, &QLocalSocket::readyRead, this,
0357         [nextConnSocket, this](){
0358             auto &info = connectionMap[nextConnSocket];
0359             switch(info.stage){
0360             case StageInitHeader:
0361                 readMessageHeader( nextConnSocket, StageInitBody );
0362                 break;
0363             case StageInitBody:
0364                 readInitMessageBody(nextConnSocket);
0365                 break;
0366             case StageConnectedHeader:
0367                 readMessageHeader( nextConnSocket, StageConnectedBody );
0368                 break;
0369             case StageConnectedBody:
0370                 this->slotDataAvailable( nextConnSocket, info.instanceId );
0371                 break;
0372             default:
0373                 break;
0374             };
0375         }
0376     );
0377 }
0378 
0379 void SingleApplicationPrivate::readMessageHeader( QLocalSocket *sock, SingleApplicationPrivate::ConnectionStage nextStage )
0380 {
0381     if (!connectionMap.contains( sock )){
0382         return;
0383     }
0384 
0385     if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ){
0386         return;
0387     }
0388 
0389     QDataStream headerStream( sock );
0390 
0391     headerStream.setVersion( QDataStream::Qt_5_6 );
0392 
0393     // Read the header to know the message length
0394     quint64 msgLen = 0;
0395     headerStream >> msgLen;
0396     ConnectionInfo &info = connectionMap[sock];
0397     info.stage = nextStage;
0398     info.msgLen = msgLen;
0399 
0400     writeAck( sock );
0401 }
0402 
0403 bool SingleApplicationPrivate::isFrameComplete( QLocalSocket *sock )
0404 {
0405     if (!connectionMap.contains( sock )){
0406         return false;
0407     }
0408 
0409     ConnectionInfo &info = connectionMap[sock];
0410     if( sock->bytesAvailable() < ( qint64 )info.msgLen ){
0411         return false;
0412     }
0413 
0414     return true;
0415 }
0416 
0417 void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock )
0418 {
0419     Q_Q(SingleApplication);
0420 
0421     if( !isFrameComplete( sock ) )
0422         return;
0423 
0424     // Read the message body
0425     QByteArray msgBytes = sock->readAll();
0426     QDataStream readStream(msgBytes);
0427 
0428     readStream.setVersion( QDataStream::Qt_5_6 );
0429 
0430     // server name
0431     QByteArray latin1Name;
0432     readStream >> latin1Name;
0433 
0434     // connection type
0435     ConnectionType connectionType = InvalidConnection;
0436     quint8 connTypeVal = InvalidConnection;
0437     readStream >> connTypeVal;
0438     connectionType = static_cast <ConnectionType>( connTypeVal );
0439 
0440     // instance id
0441     quint32 instanceId = 0;
0442     readStream >> instanceId;
0443 
0444     // checksum
0445     quint16 msgChecksum = 0;
0446     readStream >> msgChecksum;
0447 
0448     const quint16 actualChecksum = qChecksum(QByteArray(msgBytes.constData(), static_cast<quint32>(msgBytes.length() - sizeof(quint16))));
0449 
0450     bool isValid = readStream.status() == QDataStream::Ok &&
0451                    QLatin1String(latin1Name) == blockServerName &&
0452                    msgChecksum == actualChecksum;
0453 
0454     if( !isValid ){
0455         sock->close();
0456         return;
0457     }
0458 
0459     ConnectionInfo &info = connectionMap[sock];
0460     info.instanceId = instanceId;
0461     info.stage = StageConnectedHeader;
0462 
0463     if( connectionType == NewInstance ||
0464         ( connectionType == SecondaryInstance &&
0465           options & SingleApplication::Mode::SecondaryNotification ) )
0466     {
0467         Q_EMIT q->instanceStarted();
0468     }
0469 
0470     writeAck( sock );
0471 }
0472 
0473 void SingleApplicationPrivate::slotDataAvailable( QLocalSocket *dataSocket, quint32 instanceId )
0474 {
0475     Q_Q(SingleApplication);
0476 
0477     if ( !isFrameComplete( dataSocket ) )
0478         return;
0479 
0480     const QByteArray message = dataSocket->readAll();
0481 
0482     writeAck( dataSocket );
0483 
0484     ConnectionInfo &info = connectionMap[dataSocket];
0485     info.stage = StageConnectedHeader;
0486 
0487     Q_EMIT q->receivedMessage( instanceId, message);
0488 }
0489 
0490 void SingleApplicationPrivate::slotClientConnectionClosed( QLocalSocket *closedSocket, quint32 instanceId )
0491 {
0492     if( closedSocket->bytesAvailable() > 0 )
0493         slotDataAvailable( closedSocket, instanceId  );
0494 }
0495 
0496 void SingleApplicationPrivate::randomSleep()
0497 {
0498     QThread::msleep( QRandomGenerator::global()->bounded( 8u, 18u ));
0499 }
0500 
0501 void SingleApplicationPrivate::addAppData(const QString &data)
0502 {
0503     appDataList.push_back(data);
0504 }
0505 
0506 QStringList SingleApplicationPrivate::appData() const
0507 {
0508     return appDataList;
0509 }
0510 
0511 #include "moc_singleapplication_p.cpp"