You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/kopete/protocols/gadu/gadudcctransaction.cpp

455 lines
12 KiB

// -*- Mode: c++-mode; c-basic-offset: 2; indent-tabs-mode: t; tab-width: 2; -*-
//
// Copyright (C) 2004 Grzegorz Jaskiewicz <gj at pointblue.com.pl>
//
// gadudcctransaction.cpp
//
// 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.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
// 02110-1301, USA.
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>
#include "kopetetransfermanager.h"
#include "kopetemetacontact.h"
#include "kopeteuiglobal.h"
#include <tqsocketnotifier.h>
#include <tqfile.h>
#include "gadudcctransaction.h"
#include "gaducontact.h"
#include "gaduaccount.h"
#include "gadudcc.h"
#include "libgadu.h"
GaduDCCTransaction::GaduDCCTransaction( GaduDCC* parent, const char* name )
:TQObject( parent, name ), gaduDCC_( parent )
{
read_ = NULL;
write_ = NULL;
contact = NULL;
transfer_ = NULL;
dccSock_ = NULL;
peer = 0;
}
GaduDCCTransaction::~GaduDCCTransaction()
{
closeDCC();
}
unsigned int
GaduDCCTransaction::recvUIN()
{
if ( dccSock_ ) {
return dccSock_->uin;
}
return 0;
}
unsigned int
GaduDCCTransaction::peerUIN()
{
if ( dccSock_ ) {
return dccSock_->peer_uin;
}
return 0;
}
bool
GaduDCCTransaction::setupOutgoing( GaduContact* peerContact, TQString& filePath )
{
GaduContact* me;
GaduAccount* metoo;
if ( !peerContact ) {
return false;
}
me = static_cast<GaduContact*>( peerContact->account()->myself() );
TQString aaa = peerContact->contactIp().toString();
kdDebug( 14100 ) << "slotOutgoin for UIN: " << peerContact->uin() << " port " << peerContact->contactPort() << " ip " <<aaa<< endl;
kdDebug( 14100 ) << "File path is " << filePath << endl;
if ( peerContact->contactPort() >= 10 ) {
dccSock_ = gg_dcc_send_file( htonl( peerContact->contactIp().ip4Addr() ), peerContact->contactPort(), me->uin(), peerContact->uin() );
gg_dcc_fill_file_info(dccSock_,filePath.ascii());
transfer_ = Kopete::TransferManager::transferManager()->addTransfer ( peerContact,
filePath, dccSock_->file_info.size, peerContact->metaContact()->displayName(), Kopete::FileTransferInfo::Outgoing );
createNotifiers( true );
enableNotifiers( dccSock_->check );
}
else {
kdDebug( 14100 ) << "Peer " << peerContact->uin() << " is passive, requesting reverse connection" << endl;
metoo = static_cast<GaduAccount*>( me->account() );
gaduDCC_->requests[peerContact->uin()]=filePath;
metoo->dccRequest( peerContact );
}
return false;
}
bool
GaduDCCTransaction::setupIncoming( const unsigned int uin, GaduContact* peerContact )
{
if ( !peerContact ) {
kdDebug( 14100 ) << "setupIncoming called with peerContact == NULL " << endl;
return false;
}
TQString aaa = peerContact->contactIp().toString();
kdDebug( 14100 ) << "setupIncoming for UIN: " << uin << " port " << peerContact->contactPort() << " ip " <<aaa<< endl;
peer = peerContact->uin();
dccSock_ = gg_dcc_get_file( htonl( peerContact->contactIp().ip4Addr() ), peerContact->contactPort(), uin, peer );
contact = peerContact;
return setupIncoming( dccSock_ );
}
bool
GaduDCCTransaction::setupIncoming( gg_dcc* dccS )
{
if ( !dccS ) {
kdDebug(14100) << "gg_dcc_get_file failed in GaduDCCTransaction::setupIncoming" << endl;
return false;
}
dccSock_ = dccS;
peer = dccS->uin;
connect ( Kopete::TransferManager::transferManager(), TQT_SIGNAL( accepted( Kopete::Transfer *, const TQString & ) ),
this, TQT_SLOT( slotIncomingTransferAccepted ( Kopete::Transfer *, const TQString & ) ) );
connect ( Kopete::TransferManager::transferManager(), TQT_SIGNAL( refused( const Kopete::FileTransferInfo & ) ),
this, TQT_SLOT( slotTransferRefused( const Kopete::FileTransferInfo & ) ) );
incoming = true;
createNotifiers( true );
enableNotifiers( dccSock_->check );
return true;
}
void
GaduDCCTransaction::closeDCC()
{
kdDebug(14100) << "closeDCC()" << endl;
disableNotifiers();
destroyNotifiers();
gg_dcc_free( dccSock_ );
dccSock_ = NULL;
}
void
GaduDCCTransaction::destroyNotifiers()
{
disableNotifiers();
if ( read_ ) {
delete read_;
read_ = NULL;
}
if ( write_ ) {
delete write_;
write_ = NULL;
}
}
void
GaduDCCTransaction::createNotifiers( bool connect )
{
if ( !dccSock_ ){
return;
}
read_ = new TQSocketNotifier( dccSock_->fd, TQSocketNotifier::Read, this );
read_->setEnabled( false );
write_ = new TQSocketNotifier( dccSock_->fd, TQSocketNotifier::Write, this );
write_->setEnabled( false );
if ( connect ) {
TQObject::connect( read_, TQT_SIGNAL( activated( int ) ), TQT_SLOT( watcher() ) );
TQObject::connect( write_, TQT_SIGNAL( activated( int ) ), TQT_SLOT( watcher() ) );
}
}
void
GaduDCCTransaction::enableNotifiers( int checkWhat )
{
if( (checkWhat & GG_CHECK_READ) && read_ ) {
read_->setEnabled( true );
}
if( (checkWhat & GG_CHECK_WRITE) && write_ ) {
write_->setEnabled( true );
}
}
void
GaduDCCTransaction::disableNotifiers()
{
if ( read_ ) {
read_->setEnabled( false );
}
if ( write_ ) {
write_->setEnabled( false );
}
}
void
GaduDCCTransaction::slotIncomingTransferAccepted ( Kopete::Transfer* transfer, const TQString& fileName )
{
if ( (long)transfer->info().transferId () != transferId_ ) {
return;
}
transfer_ = transfer;
localFile_.setName( fileName );
if ( localFile_.exists() ) {
KGuiItem resumeButton( i18n ( "&Resume" ) );
KGuiItem overwriteButton( i18n ( "Over&write" ) );
switch ( KMessageBox::questionYesNoCancel( Kopete::UI::Global::mainWidget (),
i18n( "The file %1 already exists, do you want to resume or overwrite it?" ).arg( fileName ),
i18n( "File Exists: %1" ).arg( fileName ), resumeButton, overwriteButton ) )
{
// resume
case KMessageBox::Yes:
if ( localFile_.open( IO_WriteOnly | IO_Append ) ) {
dccSock_->offset = localFile_.size();
dccSock_->file_fd = localFile_.handle();
}
break;
// overwrite
case KMessageBox::No:
if ( localFile_.open( IO_ReadWrite ) ) {
dccSock_->offset = 0;
dccSock_->file_fd = localFile_.handle();
}
break;
// cancel
default:
closeDCC();
deleteLater();
return;
break;
}
if ( localFile_.handle() < 1 ) {
closeDCC();
deleteLater();
return;
}
}
else {
// overwrite by default
if ( localFile_.open( IO_ReadWrite ) == FALSE ) {
transfer->slotError ( KIO::ERR_COULD_NOT_WRITE, fileName );
closeDCC();
deleteLater ();
return;
}
dccSock_->offset = 0;
dccSock_->file_fd = localFile_.handle();
}
connect ( transfer, TQT_SIGNAL( result( KIO::Job * ) ), this, TQT_SLOT( slotTransferResult() ) );
// reenable notifiers
enableNotifiers( dccSock_->check );
}
void
GaduDCCTransaction::slotTransferResult()
{
if ( transfer_->error() == KIO::ERR_USER_CANCELED ) {
closeDCC();
deleteLater();
}
}
void
GaduDCCTransaction::slotTransferRefused ( const Kopete::FileTransferInfo& transfer )
{
if ( (long)transfer.transferId () != transferId_ )
return;
closeDCC();
deleteLater();
}
void
GaduDCCTransaction::askIncommingTransfer()
{
transferId_ = Kopete::TransferManager::transferManager()->askIncomingTransfer ( contact,
TQString( (const char*)dccSock_->file_info.filename ), dccSock_->file_info.size );
}
void
GaduDCCTransaction::watcher() {
gg_event* dccEvent;
GaduAccount* account;
disableNotifiers();
dccEvent = gg_dcc_watch_fd( dccSock_ );
if ( ! dccEvent ) {
// connection is bad
closeDCC();
return;
}
switch ( dccEvent->type ) {
case GG_EVENT_DCC_CLIENT_ACCEPT:
kdDebug(14100) << " GG_EVENT_DCC_CLIENT_ACCEPT " << endl;
// check dccsock->peer_uin, if unknown, oh well;
// is it for us ?
account = gaduDCC_->account( dccSock_->uin );
if ( !account ) {
kdDebug( 14100 ) << " this dcc transaction is for uin " << dccSock_->uin << ", which is not quite for me... closing" << endl;
// unknown 'to' ?, we're off
gg_free_event( dccEvent );
closeDCC();
deleteLater();
return;
}
if ( !peer ) {
contact = static_cast<GaduContact*> (account->contacts()[ TQString::number( dccSock_->peer_uin ) ]);
}
else {
contact = static_cast<GaduContact*> (account->contacts()[ TQString::number( peer ) ]);
}
if ( contact == NULL ) {
// refusing, contact on the list
kdDebug(14100) << " dcc connection from " << dccSock_->peer_uin << " refused, UIN not on the list " <<endl;
gg_free_event( dccEvent );
closeDCC();
// emit error
deleteLater();
return;
}
else {
// ask user to accept that transfer
kdDebug(14100) << " dcc accepted from " << dccSock_->peer_uin << endl;
}
break;
case GG_EVENT_DCC_CALLBACK:
kdDebug(14100) << "GG_DCC_EVENT_CALLBACK" << endl;
break;
case GG_EVENT_NONE:
kdDebug(14100) << " GG_EVENT_NONE" << endl;
// update gui with progress
if ( transfer_ ) {
transfer_->slotProcessed( dccSock_->offset );
}
break;
case GG_EVENT_DCC_NEED_FILE_ACK:
kdDebug(14100) << " GG_EVENT_DCC_NEED_FILE_ACK " << endl;
gg_free_event( dccEvent );
askIncommingTransfer();
return;
break;
case GG_EVENT_DCC_NEED_FILE_INFO:
if (gaduDCC_->requests.contains(dccSock_->peer_uin)) {
TQString filePath = gaduDCC_->requests[dccSock_->peer_uin];
kdDebug() << "Callback request found. Sending " << filePath << endl;
gaduDCC_->requests.remove(dccSock_->peer_uin);
gg_dcc_fill_file_info(dccSock_,filePath.ascii());
transfer_ = Kopete::TransferManager::transferManager()->addTransfer ( contact,
filePath, dccSock_->file_info.size, contact->metaContact()->displayName(), Kopete::FileTransferInfo::Outgoing );
} else {
gg_free_event( dccEvent );
closeDCC();
deleteLater();
return;
}
break;
case GG_EVENT_DCC_ERROR:
kdDebug(14100) << " GG_EVENT_DCC_ERROR :" << dccEvent->event.dcc_error << endl;
if ( transfer_ ) {
switch( dccEvent->event.dcc_error ) {
case GG_ERROR_DCC_REFUSED:
transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "Connection to peer was refused; it possibly does not listen for incoming connections." ) );
break;
case GG_ERROR_DCC_EOF:
transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File transfer transaction was not agreed by peer." ) );
break;
case GG_ERROR_DCC_HANDSHAKE:
transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File-transfer handshake failure." ) );
break;
case GG_ERROR_DCC_FILE:
transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "File transfer had problems with the file." ) );
break;
case GG_ERROR_DCC_NET:
transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "There was network error during file transfer." ) );
break;
default:
transfer_->slotError( KIO::ERR_SLAVE_DEFINED, i18n( "Unknown File-Transfer error." ) );
break;
}
}
gg_free_event( dccEvent );
closeDCC();
deleteLater();
return;
case GG_EVENT_DCC_DONE:
if ( transfer_ ) {
transfer_->slotComplete();
}
closeDCC();
deleteLater();
return;
default:
kdDebug(14100) << "unknown/unhandled DCC EVENT: " << dccEvent->type << endl;
break;
}
if ( dccEvent ) {
gg_free_event( dccEvent );
}
enableNotifiers( dccSock_->check );
}
#include "gadudcctransaction.moc"