/*
    Kopete Yahoo Protocol
    Send a file

    Copyright (c) 2006 André Duffeck <duffeck@kde.org>

    *************************************************************************
    *                                                                       *
    * 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 "sendfiletask.h"
#include "transfer.h"
#include "ymsgtransfer.h"
#include "yahootypes.h"
#include "client.h"
#include <tqstring.h>
#include <tqtimer.h>
#include <tdeapplication.h> // for random()
#include <kdebug.h>
#include <tdelocale.h>
#include <kstreamsocket.h>
#include <tdeio/global.h>

using namespace KNetwork;

SendFileTask::SendFileTask(Task* parent) : Task(parent)
{
	kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
	m_transmitted = 0;
	m_socket = 0;

	TQTime epoch(0, 0, 0);
}

SendFileTask::~SendFileTask()
{
	m_socket->deleteLater();
	m_socket = 0;
}

bool SendFileTask::forMe( const Transfer *transfer ) const
{
	const YMSGTransfer *t = static_cast<const YMSGTransfer*>(transfer);

	if(!t)
		return false;

	if((t->service() == Yahoo::ServiceFileTransfer7 ||
	    t->service() == Yahoo::ServiceFileTransfer7Accept) &&
	   t->firstParam(265).data() == m_yahooTransferId)
	{
		return true;
	}

	return false;
}

bool SendFileTask::take(Transfer* transfer)
{
	if( !forMe( transfer ) )
		return false;

	YMSGTransfer *t = static_cast<YMSGTransfer*>(transfer);

	kdDebug(YAHOO_RAW_DEBUG) << t->service() << endl;

	if(t->service() == Yahoo::ServiceFileTransfer7)
		parseFileTransfer(t);
	else if(t->service() == Yahoo::ServiceFileTransfer7Accept)
		parseTransferAccept(t);

	return true;
}

void SendFileTask::parseFileTransfer( const Transfer *transfer )
{
	kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;

	const YMSGTransfer *t = static_cast<const YMSGTransfer*>(transfer);

	if(!t)
		return;

	if(t->firstParam(222).toInt() == 4)
	{
		emit declined();
	}
	else if(t->firstParam(222).toInt() == 3)
	{
		sendFileTransferInfo();
	}
	else
	{
		setError();
		emit error(m_transferId, 0, i18n("Unknown error"));
	}
}

void SendFileTask::onGo()
{
	kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;

	m_file.setName( m_url.path() );

	m_yahooTransferId = newYahooTransferId();

	YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceFileTransfer7);
	t->setId( client()->sessionID() );

	t->setParam( 1, client()->userId().local8Bit() );
	t->setParam( 5, m_target.local8Bit() );
	t->setParam( 265,  m_yahooTransferId.local8Bit() );
	t->setParam( 222, 1 );
	t->setParam( 266, 1 );
	t->setParam( 302, 268 );
	t->setParam( 300, 268 );
	t->setParam( 27, m_url.fileName().local8Bit() );
	t->setParam( 28, m_file.size());
	t->setParam( 301, 268 );
	t->setParam( 303, 268 );

	send( t );
}

void SendFileTask::sendFileTransferInfo()
{
	kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;

	KResolverResults results = KResolver::resolve("filetransfer.msg.yahoo.com", TQString::number(80));
	if(results.count() > 0)
	{
		m_relayHost = results.first().address().toString();
		m_relayHost = m_relayHost.left( m_relayHost.length() - 3 ); // Remove the :80 from the end
		kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << m_relayHost << endl;
	}
	else
	{
		emit error(m_transferId, 0, i18n("Unable to connect to file transfer server"));
		setError();
		return;
	}

	YMSGTransfer *t = new YMSGTransfer(Yahoo::ServiceFileTransfer7Info);
	t->setId( client()->sessionID() );

	t->setParam( 1, client()->userId().local8Bit() );
	t->setParam( 5, m_target.local8Bit() );
	t->setParam( 265, m_yahooTransferId.local8Bit() );
	t->setParam( 27, m_url.fileName().local8Bit() );
	t->setParam( 249, 3 );
	t->setParam( 250, m_relayHost.local8Bit() );

	send( t );
}

void SendFileTask::parseTransferAccept(const Transfer *transfer)
{	
	kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;

	const YMSGTransfer *t = static_cast<const YMSGTransfer*>(transfer);

	// Disconnected
	if(t->status() == Yahoo::StatusDisconnected)
	{
		setError();
		return;
	}

	m_token = KURL::encode_string(t->firstParam(251));
	kdDebug(YAHOO_RAW_DEBUG) << "Token: " << m_token << endl;

	m_socket = new KStreamSocket( m_relayHost, TQString::number(80) );
	m_socket->setBlocking( true );
	connect( m_socket, TQT_SIGNAL( connected( const KResolverEntry& ) ), this, TQT_SLOT( connectSucceeded() ) );
	connect( m_socket, TQT_SIGNAL( gotError(int) ), this, TQT_SLOT( connectFailed(int) ) );

	m_socket->connect();

}

void SendFileTask::connectFailed( int i )
{
	TQString err = TDESocketBase::errorString(m_socket->error());
	kdDebug(YAHOO_RAW_DEBUG) << i << ": " << err << endl;
	emit error( m_transferId, i, err );
	setError();
}

void SendFileTask::connectSucceeded()
{
	kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;

	TQByteArray buffer;
	TQDataStream stream( buffer, IO_WriteOnly );

	if ( m_file.open(IO_ReadOnly ) )
	{
		kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "File successfully opened. Reading..." << endl;
	}
	else
	{
		kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Error opening file: " << TQString(m_file.errorString()).ascii() << endl;
		client()->notifyError( i18n( "An error occurred while sending the file." ), m_file.errorString(), Client::Error );
		setError();
		return;
	}

	kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Sizes: File (" << m_url << "): " << m_file.size() << endl;
	TQString header =
		"POST /relay?token=" + m_token +
		"&sender=" + client()->userId() +
		"&recver=" + m_target + " HTTP/1.1\r\n"
		"User-Agent: Mozilla/5.0\r\n"
		"Cache-Control: no-cache\r\n"
		"Cookie: T=" + client()->tCookie() + "; Y=" + client()->yCookie() + "\r\n"
		"Host: " + m_relayHost + "\r\n"
		"Content-Length: " + TQString::number(m_file.size()) + "\r\n"
		"Connection: Close\r\n\r\n";
	kdDebug() << header << endl;
	stream.writeRawBytes( header.local8Bit(), header.length() );

	if( !m_socket->writeBlock( buffer, buffer.size() ) )
	{
		emit error( m_transferId, m_socket->error(), m_socket->TDESocketBase::errorString() );
		m_socket->close();
	}
	else
	{
		connect( m_socket, TQT_SIGNAL(readyWrite()), this, TQT_SLOT(transmitData()) );
		m_socket->enableWrite( true );
	}
}

void SendFileTask::transmitData()
{
	kdDebug(YAHOO_GEN_DEBUG) << k_funcinfo << endl;
	int read = 0;
	int written = 0;	
	char buf[1024];

	m_socket->enableWrite( false );
	read = m_file.readBlock( buf, 1024 );
	written = m_socket->writeBlock( buf, read );
	kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "read:" << read << " written: " << written << endl;

	m_transmitted += read;
	emit bytesProcessed( m_transferId, m_transmitted );

	if( written != read )
	{
		kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Upload Failed!" << endl;
		emit error( m_transferId, m_socket->error(), m_socket->TDESocketBase::errorString() );
		setError();
		return;
	}
	if( m_transmitted == m_file.size() )
	{
		kdDebug(YAHOO_RAW_DEBUG) << k_funcinfo << "Upload Successful: " << m_transmitted << endl;
		emit complete( m_transferId );
		setSuccess();
		m_socket->close();
	}
	else
	{
		m_socket->enableWrite( true );
	}
}
void SendFileTask::setTarget( const TQString &to )
{
	m_target = to;
}

void SendFileTask::setMessage( const TQString &msg )
{
	m_msg = msg;
}

void SendFileTask::setFileUrl( KURL url )
{
	m_url = url;

}

void SendFileTask::setTransferId( unsigned int transferId )
{
	m_transferId = transferId;
}

void SendFileTask::canceled( unsigned int id )
{
	if( m_transferId != id )
		return;
	
	if( m_socket )
		m_socket->close();
	
	setError();
}

TQString SendFileTask::newYahooTransferId()
{
	// Adapted from libpurple/protocols/yahoo/yahoo_filexfer.c yahoo_xfer_new_xfer_id()

	TQString newId;

	for(int i = 0; i < 22; i++)
	{
		char j = TDEApplication::random() % 61;

		if(j < 26)
			newId += j + 'a';
		else if(j < 52)
			newId += j - 26 + 'A';
		else
			newId += j - 52 + '0';
	}

	newId += "$$";

	kdDebug() << "New Yahoo Transfer Id: " << newId << endl;

	return newId;
}

#include "sendfiletask.moc"