/*************************************************************************** directconnectionbase.cpp - description ------------------- begin : Tue 12 27 2005 copyright : (C) 2005 by Diederik van der Boor email : vdboor --at-- codingdomain.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "directconnectionbase.h" #include "../../utils/kmessconfig.h" #include "../../kmessdebug.h" #include <QHostAddress> #include <QTcpServer> #include <QTcpSocket> #include <QBuffer> #include <KConfigGroup> #ifdef KMESSDEBUG_DIRECTCONNECTION #define KMESSDEBUG_DIRECTCONNECTION_GENERAL #endif quint16 DirectConnectionBase::nextServerPort_( 0 ); // The constructor DirectConnectionBase::DirectConnectionBase( QObject *parent ) : QObject(parent) , authorized_(false) , isServer_(false) , lastWriteFailed_(false) , server_(0) , serverPort_(0) , socket_(0) , timeout_(false) , writeHandlerCount_(0) { connect( &connectionTimer_, SIGNAL( timeout() ), this, SLOT ( slotConnectionTimeout() )); // Read the interval of ports which we'll use to try establishing a connection KConfigGroup group = KMessConfig::instance()->getGlobalConfig( "General" ); lowestServerPortLimit_ = (quint16)group.readEntry( "lowestTransferPort", 6891 ); highestServerPortLimit_ = (quint16)group.readEntry( "highestTransferPort", 6900 ); #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Using port interval" << lowestServerPortLimit_ << "-" << highestServerPortLimit_; #endif connectionTimer_.setSingleShot( true ); } // The destructor DirectConnectionBase::~DirectConnectionBase() { // Close and clean up if(socket_ != 0 || server_ != 0) { closeConnection(); } #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "DESTROYED. [connectTo=" << connectingTo_ << " wasConnected=" << isConnected() << " wasServer=" << isServer_ << " wasAuthorized=" << authorized_ << "]"; #endif } // Close the connection void DirectConnectionBase::closeConnection() { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "closing sockets"; #endif // Reset globals // Also see slotSocketDisconnected() isServer_ = false; authorized_ = false; connectionTimer_.stop(); // Check whether we should emit a signal: bool closed = closeClientSocket() || closeServerSocket(); // Signal (only once) that the connection was closed if(closed) { emit connectionClosed(); } } // Close and delete the client socket // if the socket_ is already deleted, this method does nothing bool DirectConnectionBase::closeClientSocket() { connectionTimer_.stop(); // Notify the socket was already closed. if( socket_ == 0 ) { return false; } #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "destroying client socket."; #endif #ifdef KMESS_NETWORK_WINDOW KMESS_NET_CLOSE(this); #endif // Close the socket and delete it. // Use deleteLater since it could be called from slotSocketError() // block signals before disconnecting - otherwise we might find ourselves crashing. socket_->blockSignals( true ); socket_->disconnectFromHost(); socket_->deleteLater(); socket_ = 0; writeHandlerCount_ = 0; #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "destroyed client socket."; #endif return true; } // Close and delete the server socket bool DirectConnectionBase::closeServerSocket() { #ifdef KMESSTEST KMESS_ASSERT( socket_ == 0 || server_ != 0 ); // nextPendingConnection() turns socket_ into a child of server_ #endif connectionTimer_.stop(); // Notify the socket was already closed. if( KMESS_NULL( server_ ) ) return false; #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL if( socket_ == 0 ) { kmDebug() << "destroying server socket."; } else { kmDebug() << "closing server socket. Object will not be destroyed yet because a socket is connected."; } #endif // Close the server server_->close(); // Can't delete the object if there is still a socket_ // because socket_ becomes a qobject child of server_. if( socket_ == 0 ) { // Use deleteLater since it could be called from slotSocketError() server_->deleteLater(); server_ = 0; } return true; } // Register a slot which will be called each time the write buffer is empty (for autonomous/asynchonous writing). void DirectConnectionBase::connectWriteHandler(QObject *receiver, const char *slot) { if( KMESS_NULL(socket_) ) return; // Enable the readyWrite signal. // This is invoked each time data is written off the write-buffer. // meaning the derived class can send data fast in a autonomous way. // When you don't need to write commands very often, // this will generate a lot of of CPU overhead. // Since our socket is buffered, writeBlock() can be called directly too. // Just before KMess 1.5, we've added an internal slot here. // If a block can't be written properly, // it's remaining part will be buffered here to be sent ASAP. // That why there is a slotSocketReadyWrite() between the receiver and the socket now. if( writeHandlerCount_++ <= 0 ) { connect( socket_, SIGNAL(bytesWritten(qint64)), this, SLOT(slotSocketReadyWrite(qint64)) ); } connect( this, SIGNAL(writeHandlerReady()), receiver, slot ); // Starting with Qt 4, there is no real "readyWrite()" signal anymore, // only a bytesWritten(). We use that signal to fake the existance of a readyWrite(). // Start the first round now. emit writeHandlerReady(); } // Unregister the slot which will be called each time the write buffer is empty. void DirectConnectionBase::disconnectWriteHandler(QObject *receiver, const char *slot) { if( KMESS_NULL(socket_) ) return; // If all listeners are unset, disable the signal again. if( --writeHandlerCount_ <= 0 ) { disconnect( socket_, SIGNAL(bytesWritten(qint64)), this, SLOT(slotSocketReadyWrite(qint64)) ); } disconnect( this, SIGNAL(writeHandlerReady()), receiver, slot ); } // Return the number of bytes which are already received in the local buffer qint64 DirectConnectionBase::getAvailableBytes() const { if(KMESS_NULL(socket_)) return -2; // same as KExtendedSocket API, means "invalid state" return socket_->bytesAvailable(); } // Get the server port that will be used quint16 DirectConnectionBase::getLocalServerPort() { // Port not chosen yet if( serverPort_ == 0 ) { // It's possible to use a random port nowadays, // old clients with the "only port 6891" are no longer in use. // // KMess continues to use ports between 6891-6900 so it's easier // to firewall these. // // TODO: verify whether we can use the port by calling openServerPort() earlier. if( nextServerPort_ == 0 || nextServerPort_ > highestServerPortLimit_ ) { nextServerPort_ = lowestServerPortLimit_; } serverPort_ = nextServerPort_; nextServerPort_++; #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Reserved port " << serverPort_ << " for server socket."; #endif } return serverPort_; } // Get the remote ip the socket is connected with. QString DirectConnectionBase::getRemoteIp() const { if(socket_ == 0) { return QString::null; } QHostAddress address = socket_->peerAddress(); return address.toString(); } // Get the remote port the socket is connected with. quint16 DirectConnectionBase::getRemotePort() const { if(socket_ == 0) { return 0; } else { return socket_->peerPort(); } } // Return the error description QString DirectConnectionBase::getSocketError() const { if( timeout_ ) { return "connection timeout event"; } else if(socket_ == 0) { return QString::null; } else { return socket_->errorString(); } } // Return true if the last write action failed. bool DirectConnectionBase::hasLastWriteFailed() const { return lastWriteFailed_; } // Return true when the write buffer is full bool DirectConnectionBase::hasTemporaryWriteError() const { return ! additionalWriteBuffer_.isEmpty(); } // Find out if the connection has been inactive since 15 minutes bool DirectConnectionBase::hasTimedOut() const { // The int returned by QTime::elapsed() is in milliseconds, so it's 900 seconds or 15 min return ( lastActivity_.elapsed() > 900000 ); } // Initialize the connection, return true when this was succesful bool DirectConnectionBase::initialize() { // The base class doesn't need any initialisation, MsnDirectConnection does. return true; } // Return whether the connection login was successful. bool DirectConnectionBase::isAuthorized() const { return authorized_; } // Return true if a connection is active bool DirectConnectionBase::isConnected() const { return ( socket_ != 0 && socket_->isValid() && socket_->state() == QAbstractSocket::ConnectedState ); } // Return true if this class acts as server, false if it acts as client. bool DirectConnectionBase::isServer() const { return isServer_; } // Return true when a write handler is connnected. bool DirectConnectionBase::isWriteHandlerConnected() const { return ( writeHandlerCount_ > 0 ); } // Connect to a host bool DirectConnectionBase::openConnection( const QString &ipAddress, const quint16 port ) { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Connecting to " << ipAddress << " port " << port << "."; #endif #ifdef KMESSTEST KMESS_ASSERT( socket_ == 0 ); #endif connectingTo_ = ipAddress + ":" + QString::number(port); // Configure the socket for asynchronous operation // Note that a buffered socket only emits the readyRead() signal when data is really received. // The socket is made non-blocking, so the connectionSuccess() signal tells us when the connection is made. // Otherwise KMess would freeze until a connection is made or fails. socket_ = new QTcpSocket( this ); //KExtendedSocket(ipAddress, port, KExtendedSocket::bufferedSocket ); connect( socket_, SIGNAL( connected() ), this, SLOT ( slotSocketConnected() )); connect( socket_, SIGNAL( disconnected() ), this, SLOT ( slotSocketDisconnected() )); connect( socket_, SIGNAL( readyRead() ), this, SLOT ( slotSocketDataReceived() )); connect( socket_, SIGNAL( error(QAbstractSocket::SocketError) ), this, SLOT ( slotSocketError(QAbstractSocket::SocketError) )); // Indicate we'll become a direct-connection client. isServer_ = false; timeout_ = false; // Start timer, as the asyncConnect doesn't seam to honor the timeout value. // Needs to be started before connectToHost() to avoid a potential race condition if the call failed immediately. connectionTimer_.start( 10000 ); // Connect the socket socket_->connectToHost( ipAddress, port ); // The code continues with slotSocketConnected() or slotSocketError() return true; } // Wait for an incoming connection bool DirectConnectionBase::openServerPort() { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Creating server socket."; #endif #ifdef KMESSTEST KMESS_ASSERT( server_ == 0 ); #endif quint16 port; // Get the port port = getLocalServerPort(); connectingTo_ = "0.0.0.0:" + QString::number(port); // Create the server socket to the given port. server_ = new QTcpServer( this ); server_->setMaxPendingConnections( 1 ); connect( server_, SIGNAL( newConnection() ), this, SLOT( slotAcceptConnection() )); // Start timer, but allow a longer timeout. // Start before listen() so event's will stop the timer agian. connectionTimer_.start( 30000 ); // Indicate we'll become a direct-connection server. isServer_ = true; timeout_ = false; // Put the socket in listen mode. bool listenSuccess = server_->listen( QHostAddress::Any, port ); if( ! listenSuccess ) { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Could not listen at port " << port << ", freeing for next one."; #endif serverPort_ = 0; // reset, so getLocalServerPort() picks the next port with another openServerPort() call. delete server_; server_ = 0; return false; } // The code continues with slotAcceptConnection() return true; } // Verify how many bytes the read buffer has. Note this actually reads the data to test it. qint64 DirectConnectionBase::peekBlock( const qint64 size ) { // Avoid crashes if(KMESS_NULL(socket_)) return -1; // Read the data and see how much got read. // This is the only reliable method, but expensive in terms of performance. QByteArray buffer; buffer.resize( (int)size ); #ifdef KMESSTEST KMESS_ASSERT( size > 0 ); KMESS_ASSERT( buffer.size() == size ); #endif return socket_->peek( buffer.data(), size ); } // Read data from the socket qint64 DirectConnectionBase::readBlock( char *buffer, qint64 size ) { // Avoid crashes if(KMESS_NULL(socket_)) return -1; if(KMESS_NULL(buffer)) return -1; // Fill the buffer qint64 noBytesRead = socket_->read( buffer, size ); if( noBytesRead < 0 ) { kmWarning() << "Error while reading " << size << " bytes from socket, return code: " << noBytesRead << ", system error: " << getSocketError() << " remote address: " << getRemoteIp() << ":" << getRemotePort() << "!"; return -1; } #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL if( noBytesRead < size ) { // TODO: Even if the code detects it's not connected anymore, we don't signal this yet. // It opens a new can of worms to handle various event flows which are covered now. kmDebug() << "only " << noBytesRead << " of " << size << " bytes could be read (still connected=" << isConnected() << ")."; } #endif // Return the amount of bytes actually read return noBytesRead; } // Read data from the socket (uses the QByteArray size() as block size) qint64 DirectConnectionBase::readBlock( QByteArray &buffer, const qint64 maxSize, const qint64 bufferOffset ) { // Avoid crashes if(KMESS_NULL(socket_)) return -1; // Make final connected check if( ! isConnected() ) { kmWarning() << "Attempting to read data from a disconnected socket."; return false; } // Determine the maximum number of bytes we can write to the buffer qint64 bufferSpace = buffer.size() - bufferOffset; // API usage check. // Warn if certain sizes get a unusual value if(buffer.size() == 0) { kmWarning() << "Buffer is not initialized!"; return -1; } else if(bufferSpace < 0) { kmWarning() << "Offset of " << bufferOffset << " bytes" << " exceeds the buffer size of " << buffer.size() << " bytes!"; } else if( bufferSpace < maxSize) { kmWarning() << "Maximum allowed read size" << " exceeds the buffer size of " << buffer.size() << " bytes!"; } // Fill the buffer from the give offset, // size argument is limited to what the buffer can/may sustain. char *bufferStart = buffer.data() + bufferOffset; qint64 size = qMin( bufferSpace, maxSize ); return readBlock( bufferStart, size ); } // Read data from the socket (stored it in a QBuffer) qint64 DirectConnectionBase::readBlock( QBuffer &buffer, const qint64 maxSize ) { char charBuffer[1024]; qint64 readSize = qMin( maxSize, (qint64) 1024 ); qint64 noBytesRead = readBlock( charBuffer, readSize ); if( noBytesRead > 0 ) { buffer.write( charBuffer, noBytesRead ); } return noBytesRead; } // Mark the remote host as authorized (usually after the handshake was successful) void DirectConnectionBase::setAuthorized(bool authorized) { #ifdef KMESSTEST KMESS_ASSERT( isConnected() ); #endif // notify when it wasn't authorized before, but will be authorized now. bool notify = (! authorized_ && authorized); authorized_ = authorized; if( notify ) { emit connectionAuthorized(); } else { kmWarning() << "connection is already authorized, not notifying applications again."; } } // Accept incoming connections on the socket. void DirectConnectionBase::slotAcceptConnection() { #ifdef KMESSTEST KMESS_ASSERT( socket_ == 0 ); KMESS_ASSERT( server_ != 0 ); #endif #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Accept a socket"; #endif if(socket_ != 0) { // Already accepted a connection, close server #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Already got a connection, rejecting this request"; #endif return; } // Wait until a connection was established socket_ = server_->nextPendingConnection(); if( socket_ == 0 ) { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmWarning() << "Failed to accept incoming connection."; #endif // TODO: signal failure? return; } // Accept success #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Accept success"; #endif // Close the server, we don't need it anymore (no connectionClosed events()). // Can't delete the object here, because socket_ becomes a qobject child of server_. server_->close(); connectionTimer_.stop(); // Connect the socket signals connect( socket_, SIGNAL( disconnected() ), this, SLOT ( slotSocketDisconnected() )); connect( socket_, SIGNAL( readyRead() ), this, SLOT ( slotSocketDataReceived() )); connect( socket_, SIGNAL( error(QAbstractSocket::SocketError) ), this, SLOT ( slotSocketError(QAbstractSocket::SocketError) )); // Inform there is a connection emit connectionEstablished(); slotConnectionEstablished(); // call manually, no need to connect } // This is called when the connection is established void DirectConnectionBase::slotConnectionEstablished() { // This method is added for derived classes to overload. // It pretends we're also listening to our own signals. // This is both called when a outgoing connection was made or // an incoming connection was accepted. // By default, it's empty, } // This is called when there's an error in the socket. void DirectConnectionBase::slotConnectionFailed() { // This method is added for derived classes to overload. // It pretends we're also listening to our own signals. QString sysError( getSocketError() ); // avoids poking sys_errlist[] manually if( ! sysError.isNull() ) { kmWarning() << "Failed to connect with " << connectingTo_ << ", system error: " << sysError << "."; } else { kmWarning() << "Failed to connect with " << connectingTo_ << "!"; } // No events here, this method is called from slotSocketError() } // A timeout occured to connect a socket void DirectConnectionBase::slotConnectionTimeout() { if( isConnected() ) { // Somehow the code execution seams to get here when: // - a connection is established // - the other contact didn't send any data yet. // This could be happening before because the timer.start() was placed after the startAsyncConnect() calls. // This bug is fixed, but keep the check nevertheless. #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Timer fired but connection " "with " << connectingTo_ << " is already established, ignore."; #endif return; } #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Timer fired before connection " "with " << connectingTo_ << " was established, " "closing sockets and reporting failure."; #endif // Delete sockets, no connectionClosed events. closeClientSocket(); closeServerSocket(); // Set state for getSocketError(); timeout_ = true; connectionTimer_.stop(); // Inform derived class and other listeners // This pretends we're connected to our signals but without generating the overhead. slotConnectionFailed(); emit connectionFailed(); } // Signal that the connection was established void DirectConnectionBase::slotSocketConnected() { // This is called when the socket in openConnection() was connected. #ifdef KMESSTEST KMESS_ASSERT( ! isServer_ ); #endif #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Connected with host " << connectingTo_ << "."; #endif connectionTimer_.stop(); // Inform derived class and other listeners // This pretends we're connected to our signals but without generating the overhead. slotConnectionEstablished(); emit connectionEstablished(); } // Signal that the connection was closed void DirectConnectionBase::slotSocketDisconnected() { #ifdef KMESSTEST KMESS_ASSERT( socket_ != 0 ); #endif #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Socket got closed, state=" << socket_->state() << "."; #endif // Reset the globals. isServer_ = false; authorized_ = false; connectionTimer_.stop(); // Make sure the objects are cleaned up too. closeClientSocket(); closeServerSocket(); } // Signal that the connection could not be made. void DirectConnectionBase::slotSocketError( QAbstractSocket::SocketError error ) { // This is called when the socket in openConnection() failed, // or when the socket closed. #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "Got error with host " << connectingTo_ << " " "(code=" << error << " state=" << socket_->state() << " reason=" << socket_->errorString() << ")."; #endif connectionTimer_.stop(); // Make sure the sockets are removed. // When the class is reused, create new sockets. closeClientSocket(); closeServerSocket(); // Filter errors that happen when you've connected already. switch( error ) { case QAbstractSocket::RemoteHostClosedError: emit connectionClosed(); return; case QAbstractSocket::DatagramTooLargeError: case QAbstractSocket::NetworkError: case QAbstractSocket::UnknownSocketError: #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "No signals emitted for this error code."; #endif break; default: // Inform derived class and other listeners // This pretends we're connected to our signals but without generating the overhead. slotConnectionFailed(); emit connectionFailed(); } } // Slot called when the socket is ready to write data. void DirectConnectionBase::slotSocketReadyWrite( qint64 bytesWritten ) { // First see if the additional write buffer can be cleared. if( ! additionalWriteBuffer_.isEmpty() ) { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "flushing buffer with unsent data first. " "(bytesWritten=" << bytesWritten << " bytesToWrite=" << socket_->bytesToWrite() << " additional_bytesToWrite=" << additionalWriteBuffer_.size() << ")."; #else Q_UNUSED( bytesWritten ); #endif // TODO: some notice in the network window that we'll be sending crap now (remaining parts of a p2p packet). // Simply re-use the function. // Not the most efficient method, but the most reliable. QByteArray newBuffer = additionalWriteBuffer_; additionalWriteBuffer_.truncate(0); // See what get's sent, and what's added to additionalWriteBuffer_ again. bool success = writeBlock( newBuffer ); if( ! success ) { if( additionalWriteBuffer_.isEmpty() ) { kmWarning() << "the remaining " << newBuffer.size() << " unsent bytes could still not be sent!"; } else { // else ok, remaining is in additional buffer again. #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "buffer not completely flushed yet, " "still " << additionalWriteBuffer_.size() << " bytes to send."; #endif } // Since the data is not completely sent yet, don't signal our listeners yet. return; } #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "buffer flushed, see if the write handler can be called for more data."; #endif } // Then make sure we ask for more data. if( socket_->bytesToWrite() > 2000 ) { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "still got " << socket_->bytesToWrite() << " bytes to write, delaying next write handler signal."; #endif } else { #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmDebug() << "firing next write handler signal. " "(bytesWritten=" << bytesWritten << " bytesToWrite=" << socket_->bytesToWrite() << ")."; #endif // Notify the real senders emit writeHandlerReady(); } } /** * @brief Write data to the socket * * This function writes the packet to the socket. * If there is a write error, false is returned. * If the error is considered to be temporary, isSentBuffer * * @param block Pointer to the data block to write. * @param size Size of the block to read. * @returns Whether the has been a write error. */ 00918 bool DirectConnectionBase::writeBlock( const char *block, const qint64 size ) { lastWriteFailed_ = true; if(KMESS_NULL(socket_)) return false; // Make final connected check if( ! isConnected() ) { kmWarning() << "Attempting to write data to a disconnected socket."; return false; } // If the additional buffer is still not written, don't write next block // This would send messages in the wrong order. if( ! additionalWriteBuffer_.isEmpty() ) { #ifdef KMESSTEST KMESS_ASSERT( isWriteHandlerConnected() ); #endif // Warn! if( additionalWriteBuffer_.size() > 1000 ) { // only warn with release for larger additions. avoid false-positives for size preamble field. kmWarning() << "received another mesage of " << size << " bytes " "while the write buffer is full!"; } else { // else only warn in debug mode. #ifdef KMESSDEBUG_DIRECTCONNECTION_GENERAL kmWarning() << "received another mesage of " << size << " bytes " "while the write buffer is full!"; #endif } additionalWriteBuffer_.append( QByteArray::fromRawData( block, (int)size ) ); // Protect against consuming all system memory // 50 kB is more then enough if you're writing messages of 1300 bytes! if( additionalWriteBuffer_.size() > 50000 ) { kmWarning() << "write buffers are flooding, " "dropping connection immediately!"; additionalWriteBuffer_.truncate(0); closeConnection(); return false; } } // Write the data qint64 noBytesWritten = socket_->write( block, size ); if( noBytesWritten < 0 ) { kmWarning() << "Error while writing " << size << " bytes to socket, return code: " << noBytesWritten << ", system error: " << getSocketError() << " remote address: " << getRemoteIp() << ":" << getRemotePort() << "!"; return false; } // Log in network window. // Use actual size of written block. #ifdef KMESS_NETWORK_WINDOW QByteArray wrapper = QByteArray::fromRawData( block, (int)noBytesWritten ); KMESS_NET_SENT(this, wrapper); #endif // Warn if number of written bytes is off, // this corrupts file transfers if the data is not sent later on. if( noBytesWritten < size ) { kmWarning() << "only " << noBytesWritten << " of " << size << " bytes could be written!"; additionalWriteBuffer_.append( QByteArray::fromRawData( block + noBytesWritten, (int)(size - noBytesWritten) ) ); } else { // Finally set to positive. lastWriteFailed_ = false; } // Return true if the data was indeed written. return ( noBytesWritten == size ); } // Write data to the socket (convenience function) bool DirectConnectionBase::writeBlock( const QByteArray &block ) { return writeBlock( block.data(), block.size() ); } #include "directconnectionbase.moc"