/* kirc.cpp - IRC Client Copyright (c) 2005 by Tommi Rantala Copyright (c) 2003-2004 by Michel Hermier Copyright (c) 2002 by Nick Betcher Kopete (c) 2002-2005 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. * * * ************************************************************************* */ #ifdef HAVE_CONFIG_H #include #endif #include "kircengine.h" #include "ksslsocket.h" #include #include #include #include #include #include #include //Needed for getuid / getpwuid #include #include #include #include #ifndef KIRC_SSL_SUPPORT #define KIRC_SSL_SUPPORT #endif using namespace KIRC; // FIXME: Remove slotConnected() and error(int errCode) while going to KNetwork namespace /* Please note that the regular expression "[\\r\\n]*$" is used in a TQString::replace statement many times. * This gets rid of trailing \r\n, \r, \n, and \n\r characters. */ const TQRegExp Engine::m_RemoveLinefeeds( TQString::fromLatin1("[\\r\\n]*$") ); Engine::Engine(TQObject *parent, const char *name) : TQObject(parent, TQString::fromLatin1("[KIRC::Engine]%1").arg(name).latin1()), m_status(Idle), m_FailedNickOnLogin(false), m_useSSL(false), m_commands(101, false), // m_numericCommands(101), m_ctcpQueries(17, false), m_ctcpReplies(17, false), codecs(577,false) { setUserName(TQString()); m_commands.setAutoDelete(true); m_ctcpQueries.setAutoDelete(true); m_ctcpReplies.setAutoDelete(true); bindCommands(); bindNumericReplies(); bindCtcp(); m_VersionString = TQString::fromLatin1("Anonymous client using the KIRC engine."); m_UserString = TQString::fromLatin1("Response not supplied by user."); m_SourceString = TQString::fromLatin1("Unknown client, known source."); defaultCodec = TQTextCodec::codecForMib(106); // UTF8 mib is 106 kdDebug(14120) << "Setting default engine codec, " << defaultCodec->name() << endl; m_sock = 0L; } Engine::~Engine() { kdDebug(14120) << k_funcinfo << m_Host << endl; quit("KIRC Deleted", true); if( m_sock ) delete m_sock; } void Engine::setUseSSL( bool useSSL ) { kdDebug(14120) << k_funcinfo << useSSL << endl; if( !m_sock || useSSL != m_useSSL ) { if( m_sock ) delete m_sock; m_useSSL = useSSL; if( m_useSSL ) { #ifdef KIRC_SSL_SUPPORT m_sock = new KSSLSocket; m_sock->setSocketFlags( KExtendedSocket::inetSocket ); connect(m_sock, TQT_SIGNAL(certificateAccepted()), TQT_SLOT(slotConnected())); connect(m_sock, TQT_SIGNAL(certificateRejected()), TQT_SLOT(slotConnectionClosed())); connect(m_sock, TQT_SIGNAL(sslFailure()), TQT_SLOT(slotConnectionClosed())); } else #else kdWarning(14120) << "You tried to use SSL, but this version of Kopete was" " not compiled with IRC SSL support. A normal IRC connection will be attempted." << endl; } #endif { m_sock = new KExtendedSocket; m_sock->setSocketFlags( KExtendedSocket::inputBufferedSocket | KExtendedSocket::inetSocket ); connect(m_sock, TQT_SIGNAL(connectionSuccess()), TQT_SLOT(slotConnected())); connect(m_sock, TQT_SIGNAL(connectionFailed(int)), TQT_SLOT(error(int))); } connect(m_sock, TQT_SIGNAL(closed(int)), TQT_SLOT(slotConnectionClosed())); connect(m_sock, TQT_SIGNAL(readyRead()), TQT_SLOT(slotReadyRead())); } } void Engine::setStatus(Engine::Status status) { kdDebug(14120) << k_funcinfo << status << endl; if (m_status == status) return; // Engine::Status oldStatus = m_status; m_status = status; emit statusChanged(status); switch (m_status) { case Idle: // Do nothing. break; case Connecting: // Do nothing. break; case Authentifying: m_sock->enableRead(true); // If password is given for this server, send it now, and don't expect a reply if (!(password()).isEmpty()) pass(password()); user(m_Username, 0, m_realName); nick(m_Nickname); break; case Connected: // Do nothing. break; case Closing: m_sock->close(); m_sock->reset(); setStatus(Idle); break; case AuthentifyingFailed: setStatus(Closing); break; case Timeout: setStatus(Closing); break; case Disconnected: setStatus(Closing); break; } } void Engine::connectToServer(const TQString &host, TQ_UINT16 port, const TQString &nickname, bool useSSL ) { setUseSSL(useSSL); m_Nickname = nickname; m_Host = host; m_Port = port; kdDebug(14120) << "Trying to connect to server " << m_Host << ":" << m_Port << endl; kdDebug(14120) << "Sock status: " << m_sock->socketStatus() << endl; if( !m_sock->setAddress(m_Host, m_Port) ) kdDebug(14120) << k_funcinfo << "setAddress failed. Status: " << m_sock->socketStatus() << endl; if( m_sock->startAsyncConnect() == 0 ) { kdDebug(14120) << k_funcinfo << "Success!. Status: " << m_sock->socketStatus() << endl; setStatus(Connecting); } else { kdDebug(14120) << k_funcinfo << "Failed. Status: " << m_sock->socketStatus() << endl; setStatus(Disconnected); } } void Engine::slotConnected() { setStatus(Authentifying); } void Engine::slotConnectionClosed() { setStatus(Disconnected); } void Engine::error(int errCode) { kdDebug(14120) << k_funcinfo << "Socket error: " << errCode << endl; if (m_sock->socketStatus () != KExtendedSocket::connecting) { // Connection in progress.. This is a signal fired wrong setStatus(Disconnected); } } void Engine::setVersionString(const TQString &newString) { m_VersionString = newString; m_VersionString.remove(m_RemoveLinefeeds); } void Engine::setUserString(const TQString &newString) { m_UserString = newString; m_UserString.remove(m_RemoveLinefeeds); } void Engine::setSourceString(const TQString &newString) { m_SourceString = newString; m_SourceString.remove(m_RemoveLinefeeds); } void Engine::setUserName(const TQString &newName) { if(newName.isEmpty()) m_Username = TQString::fromLatin1(getpwuid(getuid())->pw_name); else m_Username = newName; m_Username.remove(m_RemoveLinefeeds); } void Engine::setRealName(const TQString &newName) { if(newName.isEmpty()) m_realName = TQString::fromLatin1(getpwuid(getuid())->pw_gecos); else m_realName = newName; m_realName.remove(m_RemoveLinefeeds); } bool Engine::_bind(TQDict &dict, TQString command, TQObject *object, const char *member, int minArgs, int maxArgs, const TQString &helpMessage) { // FIXME: Force upper case. // FIXME: Force number format. MessageRedirector *mr = dict[command]; if (!mr) { mr = new MessageRedirector(this, minArgs, maxArgs, helpMessage); dict.replace(command, mr); } return mr->connect(object, member); } bool Engine::bind(const TQString &command, TQObject *object, const char *member, int minArgs, int maxArgs, const TQString &helpMessage) { return _bind(m_commands, command, object, member, minArgs, maxArgs, helpMessage); } bool Engine::bind(int id, TQObject *object, const char *member, int minArgs, int maxArgs, const TQString &helpMessage) { return _bind(m_commands, TQString::number(id), object, member, minArgs, maxArgs, helpMessage); } bool Engine::bindCtcpQuery(const TQString &command, TQObject *object, const char *member, int minArgs, int maxArgs, const TQString &helpMessage) { return _bind(m_ctcpQueries, command, object, member, minArgs, maxArgs, helpMessage); } bool Engine::bindCtcpReply(const TQString &command, TQObject *object, const char *member, int minArgs, int maxArgs, const TQString &helpMessage) { return _bind(m_ctcpReplies, command, object, member, minArgs, maxArgs, helpMessage); } /* Message will be send as passed. */ void Engine::writeRawMessage(const TQString &rawMsg) { Message::writeRawMessage(this, defaultCodec, rawMsg); } /* Message will be quoted before beeing send. */ void Engine::writeMessage(const TQString &msg, const TQTextCodec *codec) { Message::writeMessage(this, codec ? codec : defaultCodec, msg); } void Engine::writeMessage(const TQString &command, const TQStringList &args, const TQString &suffix, const TQTextCodec *codec) { Message::writeMessage(this, codec ? codec : defaultCodec, command, args, suffix ); } void Engine::writeCtcpMessage(const TQString &command, const TQString &to, const TQString &ctcpMessage) { Message::writeCtcpMessage(this, defaultCodec, command, to, ctcpMessage); } void Engine::writeCtcpMessage(const TQString &command, const TQString &to, const TQString &suffix, const TQString &ctcpCommand, const TQStringList &ctcpArgs, const TQString &ctcpSuffix, bool ) { TQString nick = Entity::userNick(to); Message::writeCtcpMessage(this, codecForNick( nick ), command, nick, suffix, ctcpCommand, ctcpArgs, ctcpSuffix ); } void Engine::slotReadyRead() { // This condition is buggy when the peer server // close the socket unexpectedly bool parseSuccess; if (m_sock->socketStatus() == KExtendedSocket::connected && m_sock->canReadLine()) { Message msg = Message::parse(this, defaultCodec, &parseSuccess); if (parseSuccess) { emit receivedMessage(msg); KIRC::MessageRedirector *mr; if (msg.isNumeric()) // mr = m_numericCommands[ msg.command().toInt() ]; // we do this conversion because some dummy servers sends 1 instead of 001 // numbers are stored as "1" instead of "001" to make convertion faster (no 0 pading). mr = m_commands[ TQString::number(msg.command().toInt()) ]; else mr = m_commands[ msg.command() ]; if (mr) { TQStringList errors = mr->operator()(msg); if (!errors.isEmpty()) { kdDebug(14120) << "Method error for line:" << msg.raw() << endl; emit internalError(MethodFailed, msg); } } else if (msg.isNumeric()) { kdWarning(14120) << "Unknown IRC numeric reply for line:" << msg.raw() << endl; emit incomingUnknown(msg.raw()); } else { kdWarning(14120) << "Unknown IRC command for line:" << msg.raw() << endl; emit internalError(UnknownCommand, msg); } } else { emit incomingUnknown(msg.raw()); emit internalError(ParsingFailed, msg); } TQTimer::singleShot( 0, this, TQT_SLOT( slotReadyRead() ) ); } if(m_sock->socketStatus() != KExtendedSocket::connected) error(); } const TQTextCodec *Engine::codecForNick( const TQString &nick ) const { if( nick.isEmpty() ) return defaultCodec; TQTextCodec *codec = codecs[ nick ]; kdDebug(14120) << nick << " has codec " << codec << endl; if( !codec ) return defaultCodec; else return codec; } void Engine::showInfoDialog() { if( m_useSSL ) { static_cast( m_sock )->showInfoDialog(); } } /* * The ctcp commands seems to follow the same message behaviours has normal IRC command. * (Only missing the \n\r final characters) * So applying the same parsing rules to the messages. */ bool Engine::invokeCtcpCommandOfMessage(const TQDict &map, Message &msg) { if(msg.hasCtcpMessage() && msg.ctcpMessage().isValid()) { Message &ctcpMsg = msg.ctcpMessage(); MessageRedirector *mr = map[ctcpMsg.command()]; if (mr) { TQStringList errors = mr->operator()(msg); if (errors.isEmpty()) return true; kdDebug(14120) << "Method error for line:" << ctcpMsg.raw() << endl; writeCtcpErrorMessage(msg.prefix(), msg.ctcpRaw(), TQString::fromLatin1("%1 internal error(s)").arg(errors.size())); } else { kdDebug(14120) << "Unknow IRC/CTCP command for line:" << ctcpMsg.raw() << endl; // Don't send error message on unknown CTCP command // None of the client send it, and it makes the client as infected by virus for IRC network scanners // writeCtcpErrorMessage(msg.prefix(), msg.ctcpRaw(), "Unknown CTCP command"); emit incomingUnknownCtcp(msg.ctcpRaw()); } } else { kdDebug(14120) << "Message do not embed a CTCP message:" << msg.raw(); } return false; } EntityPtr Engine::getEntity(const TQString &name) { Entity *entity = 0; #pragma warning Do the searching code here. if (!entity) { entity = new Entity(name); m_entities.append(entity); } connect(entity, TQT_SIGNAL(destroyed(KIRC::Entity *)), TQT_SLOT(destroyed(KIRC::Entity *))); return EntityPtr(entity); } void Engine::destroyed(KIRC::Entity *entity) { m_entities.remove(entity); } void Engine::ignoreMessage(KIRC::Message &/*msg*/) { } void Engine::emitSuffix(KIRC::Message &msg) { emit receivedMessage(InfoMessage, m_server, m_server, msg.suffix()); } #include "kircengine.moc" // vim: set noet ts=4 sts=4 sw=4: