diff --git a/SingleApplication/singleapplication_p.cpp b/SingleApplication/singleapplication_p.cpp index 93112bbcf..b3553b0ee 100644 --- a/SingleApplication/singleapplication_p.cpp +++ b/SingleApplication/singleapplication_p.cpp @@ -43,9 +43,9 @@ #include "singleapplication_p.h" #ifdef Q_OS_UNIX - #include - #include - #include + #include + #include + #include #endif #ifdef Q_OS_WIN @@ -68,7 +68,7 @@ SingleApplicationPrivate::~SingleApplicationPrivate() socket->close(); delete socket; } - + memory->lock(); InstancesInfo* inst = static_cast(memory->data()); if( server != nullptr ) { @@ -79,7 +79,7 @@ SingleApplicationPrivate::~SingleApplicationPrivate() inst->checksum = blockChecksum(); } memory->unlock(); - + delete memory; } @@ -90,11 +90,11 @@ void SingleApplicationPrivate::genBlockServerName() appData.addData( SingleApplication::app_t::applicationName().toUtf8() ); appData.addData( SingleApplication::app_t::organizationName().toUtf8() ); appData.addData( SingleApplication::app_t::organizationDomain().toUtf8() ); - + if( ! (options & SingleApplication::Mode::ExcludeAppVersion) ) { appData.addData( SingleApplication::app_t::applicationVersion().toUtf8() ); } - + if( ! (options & SingleApplication::Mode::ExcludeAppPath) ) { #ifdef Q_OS_WIN appData.addData( SingleApplication::app_t::applicationFilePath().toLower().toUtf8() ); @@ -102,7 +102,7 @@ void SingleApplicationPrivate::genBlockServerName() appData.addData( SingleApplication::app_t::applicationFilePath().toUtf8() ); #endif } - + // User level block requires a user specific data in the hash if( options & SingleApplication::Mode::User ) { #ifdef Q_OS_WIN @@ -128,7 +128,7 @@ void SingleApplicationPrivate::genBlockServerName() appData.addData(username); #endif } - + // Replace the backslash in RFC 2045 Base64 [a-zA-Z0-9+/=] to comply with // server naming requirements. blockServerName = appData.result().toBase64().replace("/", "_"); @@ -146,12 +146,12 @@ void SingleApplicationPrivate::initializeMemoryBlock() void SingleApplicationPrivate::startPrimary() { Q_Q(SingleApplication); - + // Successful creation means that no main process exists // So we start a QLocalServer to listen for connections QLocalServer::removeServer( blockServerName ); server = new QLocalServer(); - + // Restrict access to the socket according to the // SingleApplication::Mode::User flag on User level or no restrictions if( options & SingleApplication::Mode::User ) { @@ -159,7 +159,7 @@ void SingleApplicationPrivate::startPrimary() } else { server->setSocketOptions( QLocalServer::WorldAccessOption ); } - + server->listen( blockServerName ); QObject::connect( server, @@ -167,14 +167,14 @@ void SingleApplicationPrivate::startPrimary() this, &SingleApplicationPrivate::slotConnectionEstablished ); - + // Reset the number of connections InstancesInfo* inst = static_cast ( memory->data() ); - + inst->primary = true; inst->primaryPid = q->applicationPid(); inst->checksum = blockChecksum(); - + instanceNumber = 0; } @@ -189,47 +189,62 @@ void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType conne if( socket == nullptr ) { socket = new QLocalSocket(); } - + // If already connected - we are done; if( socket->state() == QLocalSocket::ConnectedState ) return; - + // If not connect if( socket->state() == QLocalSocket::UnconnectedState || socket->state() == QLocalSocket::ClosingState ) { socket->connectToServer( blockServerName ); } - + // Wait for being connected if( socket->state() == QLocalSocket::ConnectingState ) { socket->waitForConnected( msecs ); } - + // Initialisation message according to the SingleApplication protocol if( socket->state() == QLocalSocket::ConnectedState ) { // Notify the parent that a new instance had been started; QByteArray initMsg; QDataStream writeStream(&initMsg, QIODevice::WriteOnly); - + #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) writeStream.setVersion(QDataStream::Qt_5_6); #endif - + writeStream << blockServerName.toLatin1(); writeStream << static_cast(connectionType); writeStream << instanceNumber; - quint16 checksum = qChecksum(initMsg.constData(), static_cast(initMsg.length())); + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove + quint16 checksum = + qChecksum( + initMsg.constData(), + static_cast(initMsg.length())); +#else +#if TODO_LIST +#pragma message("@TODO remove code for QT 6 or later") +#endif + quint16 checksum = + qChecksum( + QByteArrayView( + initMsg.constData(), + static_cast(initMsg.length()))); +#endif writeStream << checksum; - + // The header indicates the message length that follows QByteArray header; QDataStream headerStream(&header, QIODevice::WriteOnly); - + #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) headerStream.setVersion(QDataStream::Qt_5_6); #endif headerStream << static_cast ( initMsg.length() ); - + socket->write( header ); socket->write( initMsg ); socket->flush(); @@ -239,21 +254,32 @@ void SingleApplicationPrivate::connectToPrimary( int msecs, ConnectionType conne quint16 SingleApplicationPrivate::blockChecksum() { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove return qChecksum( static_cast ( memory->data() ), offsetof( InstancesInfo, checksum ) ); +#else +#if TODO_LIST +#pragma message("@TODO remove code for QT 6 or later") +#endif + return qChecksum( + QByteArrayView( + static_cast ( memory->data() ), + offsetof( InstancesInfo, checksum ))); + +#endif } qint64 SingleApplicationPrivate::primaryPid() { qint64 pid; - + memory->lock(); InstancesInfo* inst = static_cast( memory->data() ); pid = inst->primaryPid; memory->unlock(); - + return pid; } @@ -264,21 +290,21 @@ void SingleApplicationPrivate::slotConnectionEstablished() { QLocalSocket *nextConnSocket = server->nextPendingConnection(); connectionMap.insert(nextConnSocket, ConnectionInfo()); - + QObject::connect(nextConnSocket, &QLocalSocket::aboutToClose, [nextConnSocket, this]() { auto &info = connectionMap[nextConnSocket]; Q_EMIT this->slotClientConnectionClosed( nextConnSocket, info.instanceId ); } ); - + QObject::connect(nextConnSocket, &QLocalSocket::disconnected, [nextConnSocket, this](){ connectionMap.remove(nextConnSocket); nextConnSocket->deleteLater(); } ); - + QObject::connect(nextConnSocket, &QLocalSocket::readyRead, [nextConnSocket, this]() { auto &info = connectionMap[nextConnSocket]; @@ -304,24 +330,24 @@ void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock ) if (!connectionMap.contains( sock )) { return; } - + if( sock->bytesAvailable() < ( qint64 )sizeof( quint64 ) ) { return; } - + QDataStream headerStream( sock ); - + #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) headerStream.setVersion( QDataStream::Qt_5_6 ); #endif - + // Read the header to know the message length quint64 msgLen = 0; headerStream >> msgLen; ConnectionInfo &info = connectionMap[sock]; info.stage = StageBody; info.msgLen = msgLen; - + if ( sock->bytesAvailable() >= (qint64) msgLen ) { readInitMessageBody( sock ); } @@ -330,63 +356,76 @@ void SingleApplicationPrivate::readInitMessageHeader( QLocalSocket *sock ) void SingleApplicationPrivate::readInitMessageBody( QLocalSocket *sock ) { Q_Q(SingleApplication); - + if (!connectionMap.contains( sock )) { return; } - + ConnectionInfo &info = connectionMap[sock]; if( sock->bytesAvailable() < ( qint64 )info.msgLen ) { return; } - + // Read the message body QByteArray msgBytes = sock->read(info.msgLen); QDataStream readStream(msgBytes); - + #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) readStream.setVersion( QDataStream::Qt_5_6 ); #endif - + // server name QByteArray latin1Name; readStream >> latin1Name; - + // connection type ConnectionType connectionType = InvalidConnection; quint8 connTypeVal = InvalidConnection; readStream >> connTypeVal; connectionType = static_cast ( connTypeVal ); - + // instance id quint32 instanceId = 0; readStream >> instanceId; - + // checksum quint16 msgChecksum = 0; readStream >> msgChecksum; - - const quint16 actualChecksum = qChecksum( msgBytes.constData(), static_cast( msgBytes.length() - sizeof( quint16 ) ) ); - + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // ### Qt 6: remove + const quint16 actualChecksum = + qChecksum( + msgBytes.constData(), + static_cast( msgBytes.length() - sizeof( quint16 ) ) ); +#else +#if TODO_LIST +#pragma message("@TODO remove code for QT 6 or later") +#endif + const quint16 actualChecksum = + qChecksum( + QByteArrayView( + msgBytes.constData(), + static_cast(msgBytes.length() - sizeof(quint16)))); +#endif bool isValid = readStream.status() == QDataStream::Ok && QLatin1String(latin1Name) == blockServerName && msgChecksum == actualChecksum; - + if( !isValid ) { sock->close(); return; } - + info.instanceId = instanceId; info.stage = StageConnected; - + if( connectionType == NewInstance || ( connectionType == SecondaryInstance && options & SingleApplication::Mode::SecondaryNotification ) ) { Q_EMIT q->instanceStarted(); } - + if (sock->bytesAvailable() > 0) { Q_EMIT this->slotDataAvailable( sock, instanceId ); }