<commit_before>/*
* Copyright (C) 2008-2011 <NAME> <<EMAIL>>
* Copyright (C) 2010-2011 SmokeX <<EMAIL>>
*
* 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.
*
* This library 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 Lesser General Public
* License for more details.
*/

#include "ircsession.h"
#include "ircsession_p.h"
#include "irccommand.h"
#include "ircmessage.h"
#include "ircsender.h"
#include "ircutil.h"
#include "irc.h"
#include <QTcpSocket>
#include <QStringList>

/*!
    \file ircsession.h
    \brief #include &lt;IrcSession&gt;
 */

/*!
    \class IrcSession ircsession.h IrcSession
    \ingroup core
    \brief The IrcSession class provides an IRC session.

    IrcSession provides means to establish a connection to an IRC server.

    IrcSession works asynchronously ie. it is non-blocking, emitting signals
    to notify when the state of connection changes or data has arrived. The
    asynchronous approach depends on an event loop. See QCoreApplication::exec()
    for more details.

    Example usage:
    \code
    IrcSession* session = new IrcSession(this);
    connect(session, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(onMessageReceived(IrcMessage*)));
    session->setHost("irc.server.com");
    session->setUserName("me");
    session->setNickName("myself");
    session->setRealName("<NAME>");
    session->open();
    \endcode

    \sa IrcMessage and IrcCommand
 */

/*!
    \fn void IrcSession::connecting()

    This signal is emitted when the connection is being established.
 */

/*!
    \fn void IrcSession::password(QString* password)

    This signal is emitted when the connection \a password may be set.

    \note IrcSession does not store the password.
 */

/*!
    \fn void IrcSession::connected()

    This signal is emitted when the connection has been established ie.
    the welcome message has been received and the server is ready to receive commands.

    \sa Irc::RPL_WELCOME
 */

/*!
    \fn void IrcSession::disconnected()

    This signal is emitted when the session has been disconnected.
 */

/*!
    \fn void IrcSession::messageReceived(IrcMessage* message)

    This signal is emitted whenever a \a message is received.
 */

/*!
    \fn void IrcSession::nickNameChanged(const QString& name)

    This signal is emitted when the nick \a name has changed.
 */

IrcSessionPrivate::IrcSessionPrivate(IrcSession* session) :
    q_ptr(session),
    parser(),
    buffer(),
    socket(0),
    host(),
    port(6667),
    userName(),
    nickName(),
    realName()
{
}

void IrcSessionPrivate::_q_connected()
{
    Q_Q(IrcSession);
    emit q->connecting();

    QString password;
    emit q->password(&password);
    if (!password.isEmpty())
        q->sendCommand(IrcCommand::createPassword(password));

    q->sendCommand(IrcCommand::createNick(nickName));
    q->sendCommand(IrcCommand::createUser(userName, realName));
}

void IrcSessionPrivate::_q_disconnected()
{
    Q_Q(IrcSession);
    emit q->disconnected();
}

void IrcSessionPrivate::_q_reconnect()
{
    if (socket)
    {
        socket->connectToHost(host, port);
        if (socket->inherits("QSslSocket"))
            QMetaObject::invokeMethod(socket, "startClientEncryption");
    }
}

void IrcSessionPrivate::_q_error(QAbstractSocket::SocketError error)
{
    qCritical() << "IrcSessionPrivate::_q_error():" << error;
}

void IrcSessionPrivate::_q_state(QAbstractSocket::SocketState state)
{
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << "IrcSessionPrivate::_q_state():" << state;
}

void IrcSessionPrivate::_q_readData()
{
    buffer += socket->readAll();
    // try reading RFC compliant message lines first
    readLines("\r\n");
    // fall back to RFC incompliant lines...
    readLines("\n");
}

void IrcSessionPrivate::readLines(const QByteArray& delimiter)
{
    int i = -1;
    while ((i = buffer.indexOf(delimiter)) != -1)
    {
        QByteArray line = buffer.left(i).trimmed();
        buffer = buffer.mid(i + delimiter.length());
        if (!line.isEmpty())
            processLine(line);
    }
}

void IrcSessionPrivate::processLine(const QByteArray& line)
{
    Q_Q(IrcSession);
    parser.parse(line);

    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << line;

    QString prefix = parser.prefix();
    QString command = parser.command();
    QStringList params = parser.params();

    IrcMessage* msg = IrcMessage::create(command);
    if (msg)
    {
        msg->initFrom(prefix, params);

        switch (msg->type())
        {
        case IrcMessage::Numeric:
            if (static_cast<IrcNumericMessage*>(msg)->code() == Irc::RPL_WELCOME)
                emit q->connected();
            break;
        case IrcMessage::Ping: {
            QString target = static_cast<IrcPingMessage*>(msg)->target();
            IrcCommand* pongCmd = IrcCommand::createPong(target);
            q->sendCommand(pongCmd);
            break;
            }
        case IrcMessage::Nick:
            if (msg->sender().name() == nickName)
            {
                QString newNick = static_cast<IrcNickMessage*>(msg)->nick();
                if (nickName != newNick)
                {
                    nickName = newNick;
                    emit q->nickNameChanged(nickName);
                }
            }
        default:
            break;
        }

        emit q->messageReceived(msg);
    }
}

bool IrcSessionPrivate::isConnected() const
{
    return socket &&
        (socket->state() == QAbstractSocket::ConnectingState
         || socket->state() == QAbstractSocket::ConnectedState);
}

/*!
    Constructs a new IRC session with \a parent.
 */
IrcSession::IrcSession(QObject* parent) : QObject(parent), d_ptr(new IrcSessionPrivate(this))
{
    setSocket(new QTcpSocket(this));
    qRegisterMetaType<IrcSender>("IrcSender");
}

/*!
    Destructs the IRC session.
 */
IrcSession::~IrcSession()
{
    Q_D(IrcSession);<commit_msg>Fixed the memory leak, thx valgrind!!<commit_after>/*
* Copyright (C) 2008-2011 <NAME> <<EMAIL>>
* Copyright (C) 2010-2011 SmokeX <<EMAIL>>
*
* 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.
*
* This library 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 Lesser General Public
* License for more details.
*/

#include "ircsession.h"
#include "ircsession_p.h"
#include "irccommand.h"
#include "ircmessage.h"
#include "ircsender.h"
#include "ircutil.h"
#include "irc.h"
#include <QTcpSocket>
#include <QStringList>

/*!
    \file ircsession.h
    \brief #include &lt;IrcSession&gt;
 */

/*!
    \class IrcSession ircsession.h IrcSession
    \ingroup core
    \brief The IrcSession class provides an IRC session.

    IrcSession provides means to establish a connection to an IRC server.

    IrcSession works asynchronously ie. it is non-blocking, emitting signals
    to notify when the state of connection changes or data has arrived. The
    asynchronous approach depends on an event loop. See QCoreApplication::exec()
    for more details.

    Example usage:
    \code
    IrcSession* session = new IrcSession(this);
    connect(session, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(onMessageReceived(IrcMessage*)));
    session->setHost("irc.server.com");
    session->setUserName("me");
    session->setNickName("myself");
    session->setRealName("<NAME>");
    session->open();
    \endcode

    \sa IrcMessage and IrcCommand
 */

/*!
    \fn void IrcSession::connecting()

    This signal is emitted when the connection is being established.
 */

/*!
    \fn void IrcSession::password(QString* password)

    This signal is emitted when the connection \a password may be set.

    \note IrcSession does not store the password.
 */

/*!
    \fn void IrcSession::connected()

    This signal is emitted when the connection has been established ie.
    the welcome message has been received and the server is ready to receive commands.

    \sa Irc::RPL_WELCOME
 */

/*!
    \fn void IrcSession::disconnected()

    This signal is emitted when the session has been disconnected.
 */

/*!
    \fn void IrcSession::messageReceived(IrcMessage* message)

    This signal is emitted whenever a \a message is received.
 */

/*!
    \fn void IrcSession::nickNameChanged(const QString& name)

    This signal is emitted when the nick \a name has changed.
 */

IrcSessionPrivate::IrcSessionPrivate(IrcSession* session) :
    q_ptr(session),
    parser(),
    buffer(),
    socket(0),
    host(),
    port(6667),
    userName(),
    nickName(),
    realName()
{
}

void IrcSessionPrivate::_q_connected()
{
    Q_Q(IrcSession);
    emit q->connecting();

    QString password;
    emit q->password(&password);
    if (!password.isEmpty())
        q->sendCommand(IrcCommand::createPassword(password));

    q->sendCommand(IrcCommand::createNick(nickName));
    q->sendCommand(IrcCommand::createUser(userName, realName));
}

void IrcSessionPrivate::_q_disconnected()
{
    Q_Q(IrcSession);
    emit q->disconnected();
}

void IrcSessionPrivate::_q_reconnect()
{
    if (socket)
    {
        socket->connectToHost(host, port);
        if (socket->inherits("QSslSocket"))
            QMetaObject::invokeMethod(socket, "startClientEncryption");
    }
}

void IrcSessionPrivate::_q_error(QAbstractSocket::SocketError error)
{
    qCritical() << "IrcSessionPrivate::_q_error():" << error;
}

void IrcSessionPrivate::_q_state(QAbstractSocket::SocketState state)
{
    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << "IrcSessionPrivate::_q_state():" << state;
}

void IrcSessionPrivate::_q_readData()
{
    buffer += socket->readAll();
    // try reading RFC compliant message lines first
    readLines("\r\n");
    // fall back to RFC incompliant lines...
    readLines("\n");
}

void IrcSessionPrivate::readLines(const QByteArray& delimiter)
{
    int i = -1;
    while ((i = buffer.indexOf(delimiter)) != -1)
    {
        QByteArray line = buffer.left(i).trimmed();
        buffer = buffer.mid(i + delimiter.length());
        if (!line.isEmpty())
            processLine(line);
    }
}

void IrcSessionPrivate::processLine(const QByteArray& line)
{
    Q_Q(IrcSession);
    parser.parse(line);

    static bool dbg = qgetenv("COMMUNI_DEBUG").toInt();
    if (dbg) qDebug() << line;

    QString prefix = parser.prefix();
    QString command = parser.command();
    QStringList params = parser.params();

    IrcMessage* msg = IrcMessage::create(command);
    if (msg)
    {
        msg->initFrom(prefix, params);

        switch (msg->type())
        {
        case IrcMessage::Numeric:
            if (static_cast<IrcNumericMessage*>(msg)->code() == Irc::RPL_WELCOME)
                emit q->connected();
            break;
        case IrcMessage::Ping: {
            QString target = static_cast<IrcPingMessage*>(msg)->target();
            IrcCommand* pongCmd = IrcCommand::createPong(target);
            q->sendCommand(pongCmd);
            break;
            }
        case IrcMessage::Nick:
            if (msg->sender().name() == nickName)
            {
                QString newNick = static_cast<IrcNickMessage*>(msg)->nick();
                if (nickName != newNick)
                {
                    nickName = newNick;
                    emit q->nickNameChanged(nickName);
                }
            }
        default:
            break;
        }

        emit q->messageReceived(msg);
        msg->deleteLater();
    }
}

bool IrcSessionPrivate::isConnected() const
{
    return socket &&
        (socket->state() == QAbstractSocket::ConnectingState
         || socket->state() == QAbstractSocket::ConnectedState);
}

/*!
    Constructs a new IRC session with \a parent.
 */
IrcSession::IrcSession(QObject* parent) : QObject(parent), d_ptr(new IrcSessionPrivate(this))
{
    setSocket(new QTcpSocket(this));
    qRegisterMetaType<IrcSender>("IrcSender");
}

/*!
    Destructs the IRC session.
 */
IrcSession::~IrcSession()
{
    Q_D(IrcSession);
{'fraction_non_alphanumeric': 0.12325728770595691, 'fraction_numerical': 0.0038022813688212928, 'mean_word_length': 3.07520981278244, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 80, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2085321', 'n_tokens_mistral': 3895, 'n_tokens_neox': 3763, 'n_words': 1009}