/* kirc_ctcp.h - IRC Client Copyright (c) 2003 by Michel Hermier Copyright (c) 2002 by Nick Betcher Copyright (c) 2003 by Jason Keirstead Kopete (c) 2002-2003 by the Kopete developers ************************************************************************* * * * 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 "config.h" #include "kircengine.h" #include "kirctransferhandler.h" #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include #include #include using namespace KIRC; void Engine::bindCtcp() { bindCtcpQuery("ACTION", this, TQT_SLOT(CtcpQuery_action(KIRC::Message &)), -1, -1); bindCtcpQuery("CLIENTINFO", this, TQT_SLOT(CtcpQuery_clientinfo(KIRC::Message &)), -1, 1); bindCtcpQuery("DCC", this, TQT_SLOT(CtcpQuery_dcc(KIRC::Message &)), 4, 5); bindCtcpQuery("FINGER", this, TQT_SLOT(CtcpQuery_finger(KIRC::Message &)), -1, 0); bindCtcpQuery("PING", this, TQT_SLOT(CtcpQuery_ping(KIRC::Message &)), 1, 1); bindCtcpQuery("SOURCE", this, TQT_SLOT(CtcpQuery_source(KIRC::Message &)), -1, 0); bindCtcpQuery("TIME", this, TQT_SLOT(CtcpQuery_time(KIRC::Message &)), -1, 0); bindCtcpQuery("USERINFO", this, TQT_SLOT(CtcpQuery_userinfo(KIRC::Message &)), -1, 0); bindCtcpQuery("VERSION", this, TQT_SLOT(CtcpQuery_version(KIRC::Message &)), -1, 0); bindCtcpReply("ERRMSG", this, TQT_SLOT(CtcpReply_errmsg(KIRC::Message &)), 1, -1); bindCtcpReply("PING", this, TQT_SLOT(CtcpReply_ping(KIRC::Message &)), 1, 1, ""); bindCtcpReply("VERSION", this, TQT_SLOT(CtcpReply_version(KIRC::Message &)), -1, -1, ""); } // Normal order for a ctcp command: // CtcpRequest_* // CtcpQuery_* // CtcpReply_* (if any) /* Generic ctcp commnd for the /ctcp trigger */ void Engine::CtcpRequestCommand(const TQString &contact, const TQString &command) { if(m_status == Connected) { writeCtcpQueryMessage(contact, TQString(), command); // emit ctcpCommandMessage( contact, command ); } } void Engine::CtcpRequest_action(const TQString &contact, const TQString &message) { if(m_status == Connected) { writeCtcpQueryMessage(contact, TQString(), "ACTION", message); if( Entity::isChannel(contact) ) emit incomingAction(Kopete::Message::unescape(contact), Kopete::Message::unescape(m_Nickname), message); else emit incomingPrivAction(Kopete::Message::unescape(m_Nickname), Kopete::Message::unescape(contact), message); } } void Engine::CtcpQuery_action(Message &msg) { TQString target = msg.arg(0); if (target[0] == '#' || target[0] == '!' || target[0] == '&') emit incomingAction(target, msg.nickFromPrefix(), msg.ctcpMessage().ctcpRaw()); else emit incomingPrivAction(msg.nickFromPrefix(), Kopete::Message::unescape(target), msg.ctcpMessage().ctcpRaw()); } /* NO REPLY EXIST FOR THE CTCP ACTION COMMAND ! bool Engine::CtcpReply_action(Message &msg) { } */ // FIXME: the API can now answer to help commands. void Engine::CtcpQuery_clientinfo(Message &msg) { TQString clientinfo = customCtcpMap[ TQString::fromLatin1("clientinfo") ]; if (clientinfo.isNull()) clientinfo = TQString::fromLatin1("The following commands are supported, but " "without sub-command help: VERSION, CLIENTINFO, USERINFO, TIME, SOURCE, PING," "ACTION."); writeCtcpReplyMessage( msg.nickFromPrefix(), TQString(), msg.ctcpMessage().command(), TQString(), clientinfo); } void Engine::CtcpRequest_dcc(const TQString &nickname, const TQString &fileName, uint port, Transfer::Type type) { if( m_status != Connected || m_sock->localAddress() == 0 || m_sock->localAddress()->nodeName().isNull()) return; switch(type) { case Transfer::Chat: { writeCtcpQueryMessage(nickname, TQString(), TQString::fromLatin1("DCC"), TQStringList(TQString::fromLatin1("CHAT")) << TQString::fromLatin1("chat") << m_sock->localAddress()->nodeName() << TQString::number(port) ); break; } case Transfer::FileOutgoing: { TQFileInfo file(fileName); TQString noWhiteSpace = file.fileName(); if (noWhiteSpace.contains(' ') > 0) noWhiteSpace.replace(TQRegExp("\\s+"), "_"); TransferServer *server = TransferHandler::self()->createServer(this, nickname, type, fileName, file.size()); TQString ip = m_sock->localAddress()->nodeName(); TQString ipNumber = TQString::number( ntohl( inet_addr( ip.latin1() ) ) ); kdDebug(14120) << "Starting DCC file outgoing transfer." << endl; writeCtcpQueryMessage(nickname, TQString(), TQString::fromLatin1("DCC"), TQStringList(TQString::fromLatin1("SEND")) << noWhiteSpace << ipNumber << TQString::number(server->port()) << TQString::number(file.size()) ); break; } case Transfer::FileIncoming: case Transfer::Unknown: default: break; } } void Engine::CtcpQuery_dcc(Message &msg) { Message &ctcpMsg = msg.ctcpMessage(); TQString dccCommand = ctcpMsg.arg(0).upper(); if (dccCommand == TQString::fromLatin1("CHAT")) { // if(ctcpMsg.argsSize()!=4) return false; /* DCC CHAT type longip port * * type = Either Chat or Talk, but almost always Chat these days * longip = 32-bit Internet address of originator's machine * port = Port on which the originator is waitng for a DCC chat */ bool okayHost, okayPort; // should ctctMsg.arg(1) be tested? TQHostAddress address(ctcpMsg.arg(2).toUInt(&okayHost)); unsigned int port = ctcpMsg.arg(3).toUInt(&okayPort); if (okayHost && okayPort) { kdDebug(14120) << "Starting DCC chat window." << endl; TransferHandler::self()->createClient( this, msg.nickFromPrefix(), address, port, Transfer::Chat ); } } else if (dccCommand == TQString::fromLatin1("SEND")) { // if(ctcpMsg.argsSize()!=5) return false; /* DCC SEND (filename) (longip) (port) (filesize) * * filename = Name of file being sent * longip = 32-bit Internet address of originator's machine * port = Port on which the originator is waiitng for a DCC chat * filesize = Size of file being sent */ bool okayHost, okayPort, okaySize; // TQFileInfo realfile(msg.arg(1)); TQHostAddress address(ctcpMsg.arg(2).toUInt(&okayHost)); unsigned int port = ctcpMsg.arg(3).toUInt(&okayPort); unsigned int size = ctcpMsg.arg(4).toUInt(&okaySize); if (okayHost && okayPort && okaySize) { kdDebug(14120) << "Starting DCC send file transfert for file:" << ctcpMsg.arg(1) << endl; TransferHandler::self()->createClient( this, msg.nickFromPrefix(), address, port, Transfer::FileIncoming, ctcpMsg.arg(1), size ); } } // else // ((MessageRedirector *)sender())->error("Unknow dcc command"); } /* NO REPLY EXIST FOR THE CTCP DCC COMMAND ! bool Engine::CtcpReply_dcc(Message &msg) { } */ void Engine::CtcpReply_errmsg(Message &) { // should emit one signal } void Engine::CtcpQuery_finger( Message &) { // To be implemented } void Engine::CtcpRequest_ping(const TQString &target) { kdDebug(14120) << k_funcinfo << endl; timeval time; if (gettimeofday(&time, 0) == 0) { TQString timeReply; if( Entity::isChannel(target) ) timeReply = TQString::fromLatin1("%1.%2").arg(time.tv_sec).arg(time.tv_usec); else timeReply = TQString::number( time.tv_sec ); writeCtcpQueryMessage( target, TQString(), "PING", timeReply); } // else // ((MessageRedirector *)sender())->error("failed to get current time"); } void Engine::CtcpQuery_ping(Message &msg) { writeCtcpReplyMessage( msg.nickFromPrefix(), TQString(), msg.ctcpMessage().command(), msg.ctcpMessage().arg(0)); } void Engine::CtcpReply_ping(Message &msg) { timeval time; if (gettimeofday(&time, 0) == 0) { // FIXME: the time code is wrong for usec TQString timeReply = TQString::fromLatin1("%1.%2").arg(time.tv_sec).arg(time.tv_usec); double newTime = timeReply.toDouble(); double oldTime = msg.suffix().section(' ',0, 0).toDouble(); double difference = newTime - oldTime; TQString diffString; if (difference < 1) { diffString = TQString::number(difference); diffString.remove((diffString.find('.') -1), 2); diffString.truncate(3); diffString.append("milliseconds"); } else { diffString = TQString::number(difference); TQString seconds = diffString.section('.', 0, 0); TQString millSec = diffString.section('.', 1, 1); millSec.remove(millSec.find('.'), 1); millSec.truncate(3); diffString = TQString::fromLatin1("%1 seconds, %2 milliseconds").arg(seconds).arg(millSec); } emit incomingCtcpReply(TQString::fromLatin1("PING"), msg.nickFromPrefix(), diffString); } // else // ((MessageRedirector *)sender())->error("failed to get current time"); } void Engine::CtcpQuery_source(Message &msg) { writeCtcpReplyMessage(msg.nickFromPrefix(), TQString(), msg.ctcpMessage().command(), m_SourceString); } void Engine::CtcpQuery_time(Message &msg) { writeCtcpReplyMessage(msg.nickFromPrefix(), TQString(), msg.ctcpMessage().command(), TQDateTime::currentDateTime().toString(), TQString(), false); } void Engine::CtcpQuery_userinfo(Message &msg) { TQString userinfo = customCtcpMap[ TQString::fromLatin1("userinfo") ]; if (userinfo.isNull()) userinfo = m_UserString; writeCtcpReplyMessage(msg.nickFromPrefix(), TQString(), msg.ctcpMessage().command(), TQString(), userinfo); } void Engine::CtcpRequest_version(const TQString &target) { writeCtcpQueryMessage(target, TQString(), "VERSION"); } void Engine::CtcpQuery_version(Message &msg) { TQString response = customCtcpMap[ TQString::fromLatin1("version") ]; kdDebug(14120) << "Version check: " << response << endl; if (response.isNull()) response = m_VersionString; writeCtcpReplyMessage(msg.nickFromPrefix(), msg.ctcpMessage().command() + " " + response); } void Engine::CtcpReply_version(Message &msg) { emit incomingCtcpReply(msg.ctcpMessage().command(), msg.nickFromPrefix(), msg.ctcpMessage().ctcpRaw()); }