/* Kopete Yahoo Protocol Handles incoming webcam connections Copyright (c) 2005 André Duffeck ************************************************************************* * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2 of the License, or (at your option) any later version. * * * ************************************************************************* */ #include "webcamtask.h" #include "sendnotifytask.h" #include "transfer.h" #include "ymsgtransfer.h" #include "yahootypes.h" #include "client.h" #include #include #include #include #include #include #include #include #include using namespace KNetwork; WebcamTask::WebcamTask(Task* parent) : Task(parent) { kdDebug(YAHOO_RAW_DEBUG) ; transmittingData = false; transmissionPending = false; timestamp = 1; } WebcamTask::~WebcamTask() { } bool WebcamTask::take( Transfer* transfer ) { if ( !forMe( transfer ) ) return false; YMSGTransfer *t = static_cast(transfer); if( t->service() == Yahoo::ServiceWebcam ) parseWebcamInformation( t ); // else // parseMessage( transfer ); return true; } bool WebcamTask::forMe( const Transfer* transfer ) const { const YMSGTransfer *t = 0L; t = dynamic_cast(transfer); if (!t) return false; if ( t->service() == Yahoo::ServiceWebcam ) return true; else return false; } void WebcamTask::requestWebcam( const TQString &who ) { kdDebug(YAHOO_RAW_DEBUG) ; YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceWebcam); t->setId( client()->sessionID() ); t->setParam( 1, client()->userId().local8Bit()); t->setParam( 5, who.local8Bit() ); keyPending = who; send( t ); } void WebcamTask::parseWebcamInformation( YMSGTransfer *t ) { kdDebug(YAHOO_RAW_DEBUG) ; YahooWebcamInformation info; info.sender = keyPending; info.server = t->firstParam( 102 ); info.key = t->firstParam( 61 ); info.status = InitialStatus; info.dataLength = 0; info.buffer = 0L; info.headerRead = false; if( info.sender == client()->userId() ) { transmittingData = true; info.direction = Outgoing; } else info.direction = Incoming; kdDebug(YAHOO_RAW_DEBUG) << "Got WebcamInformation: Sender: " << info.sender << " Server: " << info.server << " Key: " << info.key << endl; KStreamSocket *socket = new KStreamSocket( info.server, TQString::number(5100) ); socketMap[socket] = info; socket->enableRead( true ); connect( socket, TQ_SIGNAL( connected( const KResolverEntry& ) ), this, TQ_SLOT( slotConnectionStage1Established() ) ); connect( socket, TQ_SIGNAL( gotError(int) ), this, TQ_SLOT( slotConnectionFailed(int) ) ); connect( socket, TQ_SIGNAL( readyRead() ), this, TQ_SLOT( slotRead() ) ); socket->connect(); } void WebcamTask::slotConnectionStage1Established() { KStreamSocket* socket = const_cast( dynamic_cast( sender() ) ); if( !socket ) return; kdDebug(YAHOO_RAW_DEBUG) << "Webcam connection Stage1 to the user " << socketMap[socket].sender << " established." << endl; disconnect( socket, TQ_SIGNAL( connected( const KResolverEntry& ) ), this, TQ_SLOT( slotConnectionStage1Established() ) ); disconnect( socket, TQ_SIGNAL( gotError(int) ), this, TQ_SLOT( slotConnectionFailed(int) ) ); socketMap[socket].status = ConnectedStage1; TQByteArray buffer; TQDataStream stream( buffer, IO_WriteOnly ); TQString s; if( socketMap[socket].direction == Incoming ) { socket->writeBlock( TQCString("").data(), 8 ); s = TQString("g=%1\r\n").arg(socketMap[socket].sender); } else { socket->writeBlock( TQCString("").data(), 8 ); s = TQString("f=1\r\n"); } // Header: 08 00 01 00 00 00 00 stream << (TQ_INT8)0x08 << (TQ_INT8)0x00 << (TQ_INT8)0x01 << (TQ_INT8)0x00 << (TQ_INT32)s.length(); stream.writeRawBytes( s.local8Bit(), s.length() ); socket->writeBlock( buffer.data(), buffer.size() ); } void WebcamTask::slotConnectionStage2Established() { KStreamSocket* socket = const_cast( dynamic_cast( sender() ) ); if( !socket ) return; kdDebug(YAHOO_RAW_DEBUG) << "Webcam connection Stage2 to the user " << socketMap[socket].sender << " established." << endl; disconnect( socket, TQ_SIGNAL( connected( const KResolverEntry& ) ), this, TQ_SLOT( slotConnectionStage2Established() ) ); disconnect( socket, TQ_SIGNAL( gotError(int) ), this, TQ_SLOT( slotConnectionFailed(int) ) ); socketMap[socket].status = ConnectedStage2; TQByteArray buffer; TQDataStream stream( buffer, IO_WriteOnly ); TQString s; if( socketMap[socket].direction == Incoming ) { // Send -Packet socket->writeBlock( TQCString("").data(), 8 ); // Send request information s = TQString("a=2\r\nc=us\r\ne=21\r\nu=%1\r\nt=%2\r\ni=\r\ng=%3\r\no=w-2-5-1\r\np=1") .arg(client()->userId()).arg(socketMap[socket].key).arg(socketMap[socket].sender); // Header: 08 00 01 00 00 00 00 stream << (TQ_INT8)0x08 << (TQ_INT8)0x00 << (TQ_INT8)0x01 << (TQ_INT8)0x00 << (TQ_INT32)s.length(); } else { // Send -Packet socket->writeBlock( TQCString("").data(), 8 ); // Send request information s = TQString("a=2\r\nc=us\r\nu=%1\r\nt=%2\r\ni=%3\r\no=w-2-5-1\r\np=2\r\nb=KopeteWebcam\r\nd=\r\n") .arg(client()->userId()).arg(socketMap[socket].key).arg(socket->localAddress().nodeName()); // Header: 08 00 05 00 00 00 00 01 00 00 00 01 stream << (TQ_INT8)0x0d << (TQ_INT8)0x00 << (TQ_INT8)0x05 << (TQ_INT8)0x00 << (TQ_INT32)s.length() << (TQ_INT8)0x01 << (TQ_INT8)0x00 << (TQ_INT8)0x00 << (TQ_INT8)0x00 << (TQ_INT8)0x01; } socket->writeBlock( buffer.data(), buffer.size() ); socket->writeBlock( s.local8Bit(), s.length() ); } void WebcamTask::slotConnectionFailed( int error ) { KStreamSocket* socket = const_cast( dynamic_cast( sender() ) ); kdDebug(YAHOO_RAW_DEBUG) << "Webcam connection to the user " << socketMap[socket].sender << " failed. Error " << error << " - " << socket->TDESocketBase::errorString() << endl; client()->notifyError( i18n("Webcam connection to the user %1 could not be established.\n\nPlease relogin and try again.") .arg(socketMap[socket].sender), TQString("%1 - %2").arg(error).arg( socket->TDESocketBase::errorString()), Client::Error ); socketMap.remove( socket ); socket->deleteLater(); } void WebcamTask::slotRead() { KStreamSocket* socket = const_cast( dynamic_cast( sender() ) ); if( !socket ) return; switch( socketMap[socket].status ) { case ConnectedStage1: disconnect( socket, TQ_SIGNAL( readyRead() ), this, TQ_SLOT( slotRead() ) ); connectStage2( socket ); break; case ConnectedStage2: case Sending: case SendingEmpty: processData( socket ); default: break; } } void WebcamTask::connectStage2( KStreamSocket *socket ) { kdDebug(YAHOO_RAW_DEBUG) ; TQByteArray data( socket->bytesAvailable() ); socket->readBlock ( data.data (), data.size () ); kdDebug(YAHOO_RAW_DEBUG) << "Magic Byte:" << data[2] << endl; socketMap[socket].status = ConnectedStage2; TQString server; int i = 4; KStreamSocket *newSocket; switch( (const char)data[2] ) { case (TQ_INT8)0x06: emit webcamNotAvailable(socketMap[socket].sender); break; case (TQ_INT8)0x04: case (TQ_INT8)0x07: while( (const char)data[i] != (TQ_INT8)0x00 ) server += data[i++]; kdDebug(YAHOO_RAW_DEBUG) << "Server:" << server << endl; if( server.isEmpty() ) { emit webcamNotAvailable(socketMap[socket].sender); break; } kdDebug(YAHOO_RAW_DEBUG) << "Connecting to " << server << endl; newSocket = new KStreamSocket( server, TQString::number(5100) ); socketMap[newSocket] = socketMap[socket]; newSocket->enableRead( true ); connect( newSocket, TQ_SIGNAL( connected( const KResolverEntry& ) ), this, TQ_SLOT( slotConnectionStage2Established() ) ); connect( newSocket, TQ_SIGNAL( gotError(int) ), this, TQ_SLOT( slotConnectionFailed(int) ) ); connect( newSocket, TQ_SIGNAL( readyRead() ), this, TQ_SLOT( slotRead() ) ); if( socketMap[newSocket].direction == Outgoing ) { newSocket->enableWrite( true ); connect( newSocket, TQ_SIGNAL( readyWrite() ), this, TQ_SLOT( transmitWebcamImage() ) ); } newSocket->connect(); break; default: break; } socketMap.remove( socket ); delete socket; } void WebcamTask::processData( KStreamSocket *socket ) { TQByteArray data( socket->bytesAvailable() ); socket->readBlock ( data.data (), data.size () ); if( data.size() <= 0 ) { kdDebug(YAHOO_RAW_DEBUG) << "No data read." << endl; return; } parseData( data, socket ); } void WebcamTask::parseData( TQByteArray &data, KStreamSocket *socket ) { int headerLength = 0; int read = 0; YahooWebcamInformation *info = &socketMap[socket]; if( !info->headerRead ) { headerLength = data[0]; kdDebug(YAHOO_RAW_DEBUG) << "headerLength " << headerLength << endl; if( data.size() < headerLength ) return; if( headerLength >= 8 ) { kdDebug() << data[0] << data[1] << data[2] << data[3] << data[4] << data[5] << data[6] << data[7] << endl; info->reason = data[1]; info->dataLength = yahoo_get32(data.data() + 4); } if( headerLength == 13 ) { kdDebug() << data[8] << data[9] << data[10] << data[11] << data[12] << endl; info->timestamp = yahoo_get32(data.data() + 9); kdDebug(YAHOO_RAW_DEBUG) << "PacketType: " << data[8] << " reason: " << info->reason << " timestamp: " << info->timestamp << endl; TQStringList::iterator it; switch( data[8] ) { case 0x00: if( info->direction == Incoming ) { if( info->timestamp == 0 ) { emit webcamClosed( info->sender, 3 ); cleanUpConnection( socket ); } } else { info->type = UserRequest; info->headerRead = true; } break; case 0x02: info->type = Image; info->headerRead = true; break; case 0x04: if( info->timestamp == 1 ) { emit webcamPaused( info->sender ); } break; case 0x05: kdDebug(YAHOO_RAW_DEBUG) << "Ready for Transmission" << endl; if( info->timestamp == 1 ) { info->status = Sending; emit readyForTransmission(); } else if( info->timestamp == 0 ) { info->status = SendingEmpty; emit stopTransmission(); sendEmptyWebcamImage(); } // Send Invitation packets for(it = pendingInvitations.begin(); it != pendingInvitations.end(); it++) { SendNotifyTask *snt = new SendNotifyTask( parent() ); snt->setTarget( *it ); snt->setType( SendNotifyTask::NotifyWebcamInvite ); snt->go( true ); it = pendingInvitations.erase( it ); it--; } break; case 0x07: info->type = ConnectionClosed; emit webcamClosed( info->sender, info->reason ); cleanUpConnection( socket ); case 0x0c: info->type = NewWatcher; info->headerRead = true; break; case 0x0d: info->type = WatcherLeft; info->headerRead = true; break; } } if( headerLength > 13 || headerLength <= 0) //Parse error return; if( !info->headerRead && data.size() > headerLength ) { // More headers to read kdDebug(YAHOO_RAW_DEBUG) << "More data to read..." << endl; TQByteArray newData( data.size() - headerLength ); TQDataStream stream( newData, IO_WriteOnly ); stream.writeRawBytes( data.data() + headerLength, data.size() - headerLength ); parseData( newData, socket ); return; } kdDebug(YAHOO_RAW_DEBUG) << "Parsed Packet: HeaderLen: " << headerLength << " DataLen: " << info->dataLength << endl; } if( info->dataLength <= 0 ) { kdDebug(YAHOO_RAW_DEBUG) << "No data to read. (info->dataLength <= 0)" << endl; if( info->headerRead ) info->headerRead = false; return; } if( headerLength >= data.size() ) { kdDebug(YAHOO_RAW_DEBUG) << "No data to read. (headerLength >= data.size())" << endl; return; //Nothing to read here... } if( !info->buffer ) { kdDebug(YAHOO_RAW_DEBUG) << "Buffer created" << endl; info->buffer = new TQBuffer(); info->buffer->open( IO_WriteOnly ); } kdDebug(YAHOO_RAW_DEBUG) << "data.size() " << data.size() << " headerLength " << headerLength << " buffersize " << info->buffer->size() << endl; read = headerLength + info->dataLength - info->buffer->size(); info->buffer->writeBlock( data.data() + headerLength, data.size() - headerLength );//info->dataLength - info->buffer->size() ); kdDebug(YAHOO_RAW_DEBUG) << "read " << data.size() - headerLength << " Bytes, Buffer is now " << info->buffer->size() << endl; if( info->buffer->size() >= static_cast(info->dataLength) ) { info->buffer->close(); TQString who; switch( info->type ) { case UserRequest: { who.append( info->buffer->buffer() ); who = who.mid( 2, who.find('\n') - 3); kdDebug(YAHOO_RAW_DEBUG) << "User wants to view webcam: " << who << " len: " << who.length() << " Index: " << accessGranted.findIndex( who ) << endl; if( accessGranted.findIndex( who ) >= 0 ) { grantAccess( who ); } else emit viewerRequest( who ); } break; case NewWatcher: who.append( info->buffer->buffer() ); who = who.left( who.length() - 1 ); kdDebug(YAHOO_RAW_DEBUG) << "New Watcher of webcam: " << who << endl; emit viewerJoined( who ); break; case WatcherLeft: who.append( info->buffer->buffer() ); who = who.left( who.length() - 1 ); kdDebug(YAHOO_RAW_DEBUG) << "A Watcher left: " << who << " len: " << who.length() << endl; accessGranted.remove( who ); emit viewerLeft( who ); break; case Image: { TQPixmap webcamImage; //webcamImage.loadFromData( info->buffer->buffer() ); // FIXME (same) //KTemporaryFile jpcTmpImageFile; //jpcTmpImageFile.setAutoRemove(false); //jpcTmpImageFile.open(); //KTemporaryFile bmpTmpImageFile; //bmpTmpImageFile.setAutoRemove(false); //bmpTmpImageFile.open(); //jpcTmpImageFile.write((info->buffer->buffer()).data(), info->buffer->size()); //jpcTmpImageFile.close(); KTempFile jpcTmpImageFile; KTempFile bmpTmpImageFile; TQFile *file = jpcTmpImageFile.file(); file->writeBlock((info->buffer->buffer()).data(), info->buffer->size()); file->close(); TDEProcess p; p << "jasper"; p << "--input" << jpcTmpImageFile.name() << "--output" << bmpTmpImageFile.name() << "--output-format" << "bmp"; p.start( TDEProcess::Block ); if( p.exitStatus() != 0 ) { kdDebug(YAHOO_RAW_DEBUG) << " jasper exited with status " << p.exitStatus() << " " << info->sender << endl; } else { webcamImage.load( bmpTmpImageFile.name() ); /******* UPTO THIS POINT ******/ emit webcamImageReceived( info->sender, webcamImage ); } TQFile::remove(jpcTmpImageFile.name()); TQFile::remove(bmpTmpImageFile.name()); kdDebug(YAHOO_RAW_DEBUG) << "Image Received. Size: " << webcamImage.size() << endl; } break; default: break; } info->headerRead = false; delete info->buffer; info->buffer = 0L; } if( data.size() > read ) { // More headers to read kdDebug(YAHOO_RAW_DEBUG) << "More data to read..." << data.size() - read << endl; TQByteArray newData( data.size() - read ); TQDataStream stream( newData, IO_WriteOnly ); stream.writeRawBytes( data.data() + read, data.size() - read ); parseData( newData, socket ); } } void WebcamTask::cleanUpConnection( KStreamSocket *socket ) { socket->close(); YahooWebcamInformation *info = &socketMap[socket]; if( info->buffer ) delete info->buffer; socketMap.remove( socket ); delete socket; } void WebcamTask::closeWebcam( const TQString & who ) { kdDebug(YAHOO_RAW_DEBUG) ; SocketInfoMap::Iterator it; for( it = socketMap.begin(); it != socketMap.end(); it++ ) { kdDebug(YAHOO_RAW_DEBUG) << it.data().sender << " - " << who << endl; if( it.data().sender == who ) { cleanUpConnection( it.key() ); return; } } kdDebug(YAHOO_RAW_DEBUG) << "Error. You tried to close a connection that did not exist." << endl; client()->notifyError( i18n( "An error occurred closing the webcam session. " ), i18n( "You tried to close a connection that did not exist." ), Client::Debug ); } // Sending void WebcamTask::registerWebcam() { kdDebug(YAHOO_RAW_DEBUG) ; YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceWebcam); t->setId( client()->sessionID() ); t->setParam( 1, client()->userId().local8Bit()); keyPending = client()->userId(); send( t ); } void WebcamTask::addPendingInvitation( const TQString &userId ) { kdDebug(YAHOO_RAW_DEBUG) << "Inviting " << userId << " to watch the webcam." << endl; pendingInvitations.append( userId ); accessGranted.append( userId ); } void WebcamTask::grantAccess( const TQString &userId ) { kdDebug(YAHOO_RAW_DEBUG) ; KStreamSocket *socket = 0L; SocketInfoMap::Iterator it; for( it = socketMap.begin(); it != socketMap.end(); it++ ) { if( it.data().direction == Outgoing ) { socket = it.key(); break; } } if( !socket ) { kdDebug(YAHOO_RAW_DEBUG) << "Error. No outgoing socket found." << endl; return; } TQByteArray ar; TQDataStream stream( ar, IO_WriteOnly ); TQString user = TQString("u=%1").arg(userId); stream << (TQ_INT8)0x0d << (TQ_INT8)0x00 << (TQ_INT8)0x05 << (TQ_INT8)0x00 << (TQ_INT32)user.length() << (TQ_INT8)0x00 << (TQ_INT8)0x00 << (TQ_INT8)0x00 << (TQ_INT8)0x00 << (TQ_INT8)0x01; socket->writeBlock( ar.data(), ar.size() ); socket->writeBlock( user.local8Bit(), user.length() ); } void WebcamTask::closeOutgoingWebcam() { kdDebug(YAHOO_RAW_DEBUG) ; KStreamSocket *socket = 0L; SocketInfoMap::Iterator it; for( it = socketMap.begin(); it != socketMap.end(); it++ ) { if( it.data().direction == Outgoing ) { socket = it.key(); break; } } if( !socket ) { kdDebug(YAHOO_RAW_DEBUG) << "Error. No outgoing socket found." << endl; return; } cleanUpConnection( socket ); transmittingData = false; } void WebcamTask::sendEmptyWebcamImage() { kdDebug(YAHOO_RAW_DEBUG) ; KStreamSocket *socket = 0L; SocketInfoMap::Iterator it; for( it = socketMap.begin(); it != socketMap.end(); it++ ) { if( it.data().direction == Outgoing ) { socket = it.key(); break; } } if( !socket ) { kdDebug(YAHOO_RAW_DEBUG) << "Error. No outgoing socket found." << endl; return; } if( socketMap[socket].status != SendingEmpty ) return; pictureBuffer.resize( 0 ); transmissionPending = true; TQTimer::singleShot( 1000, this, TQ_SLOT(sendEmptyWebcamImage()) ); } void WebcamTask::sendWebcamImage( const TQByteArray &image ) { kdDebug(YAHOO_RAW_DEBUG) ; pictureBuffer = image; transmissionPending = true; KStreamSocket *socket = 0L; SocketInfoMap::Iterator it; for( it = socketMap.begin(); it != socketMap.end(); it++ ) { if( it.data().direction == Outgoing ) { socket = it.key(); break; } } if( !socket ) { kdDebug(YAHOO_RAW_DEBUG) << "Error. No outgoing socket found." << endl; return; } socket->enableWrite( true ); } void WebcamTask::transmitWebcamImage() { if( !transmissionPending ) return; kdDebug(YAHOO_RAW_DEBUG) << "arraysize: " << pictureBuffer.size() << endl; // Find outgoing socket KStreamSocket *socket = 0L; SocketInfoMap::Iterator it; for( it = socketMap.begin(); it != socketMap.end(); it++ ) { if( it.data().direction == Outgoing ) { socket = it.key(); break; } } if( !socket ) { kdDebug(YAHOO_RAW_DEBUG) << "Error. No outgoing socket found." << endl; return; } socket->enableWrite( false ); TQByteArray buffer; TQDataStream stream( buffer, IO_WriteOnly ); stream << (TQ_INT8)0x0d << (TQ_INT8)0x00 << (TQ_INT8)0x05 << (TQ_INT8)0x00 << (TQ_INT32)pictureBuffer.size() << (TQ_INT8)0x02 << (TQ_INT32)timestamp++; socket->writeBlock( buffer.data(), buffer.size() ); if( pictureBuffer.size() ) socket->writeBlock( pictureBuffer.data(), pictureBuffer.size() ); transmissionPending = false; } #include "webcamtask.moc"