view gui/src/qirc/IRCClientImpl.cpp @ 13598:a9b96f66202a

Further reworked on IRC backend.
author Jacob Dawid <jacob.dawid@googlemail.com>
date Sun, 14 Aug 2011 23:36:38 +0200
parents 73a9ac1cdbf1
children e67616aca5a6
line wrap: on
line source

/* OctaveGUI - A graphical user interface for Octave
 * Copyright (C) 2011 Jacob Dawid
 * jacob.dawid@googlemail.com
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

#include "IRCClientImpl.h"

IRCServerMessage::IRCServerMessage (const QString& serverMessage)
{
  const char *message = serverMessage.toStdString().c_str();
  char prefix[512];
  int index = 0;

  m_nick = "";
  m_user = "";
  m_host = "";
  m_parameters.reserve (16);

  if (message[0] == CHR_COLON)
    {
      index++;
      strcpy (prefix,
              getStringToken (message, index).toStdString ().c_str ());

      int etapa = 0;
      for (unsigned int i = 0; i < strlen (prefix); i++)
        {
          switch (prefix[i])
            {
            case '!':
              etapa = 1;
              break;
            case '@':
              etapa = 2;
              break;
            default:
              switch (etapa)
                {
                case 0:
                  m_nick += prefix[i];
                  break;
                case 1:
                  m_user += prefix[i];
                  break;
                default:
                  m_host += prefix[i];
                  break;
                }
            }
        }
    }

  m_command = getStringToken (message, index);
  m_command = m_command.toUpper ();

  while (message[index] != 0)
    {
      if ((message[index] == CHR_COLON))
        {

          if (message[index] == CHR_COLON)
            {
              index++;
            }

          m_parameters.append ( (const char *) (message + index));
          index += strlen (message + index);
        }
      else
        {
          m_parameters.append (getStringToken (message, index));
        }
    }

  if (strlen (m_command.toStdString ().c_str ()) ==
      strspn (m_command.toStdString ().c_str (), DIGITS))
    {
      m_isNumeric = true;
      m_codeNumber = atoi (m_command.toStdString ().c_str ());
    }
  else
    {
      m_isNumeric = false;
    }
}

int
IRCServerMessage::numericValue ()
{
  if (m_isNumeric)
    return m_codeNumber;
  return -1;
}

QString
IRCServerMessage::parameter (int index)
{
  if (index >= 0 && index < m_parameters.size ())
    return m_parameters.at (index);
  return "";
}

int
IRCServerMessage::skipSpaces (const char *line, int &index)
{
  while (line[index] == CHR_SPACE)
    {
      index++;
    }
  return index;
}

QString
IRCServerMessage::getStringToken (const char *line, int &index)
{
  QString token ("");
  skipSpaces (line, index);
  while ((line[index] != CHR_SPACE) && (line[index] != CHR_ZERO))
    {
      token += line[index];
      index++;
    }

  skipSpaces (line, index);
  return token;
}

QString
IRCServerMessage::getStringToken (QString line, int &index)
{
  return getStringToken (line.toStdString ().c_str (), index);
}

IRCChannelProxy::IRCChannelProxy (IRCClientInterface *clientInterface, const QString& channelName, QObject *parent)
  : IRCChannelProxyInterface (clientInterface, channelName, parent),
    m_clientInterface (clientInterface)
{
  m_channelName = channelName;
}

QTextDocument *
IRCChannelProxy::conversationModel ()
{
  return &m_conversationModel;
}

QStringListModel *
IRCChannelProxy::userListModel ()
{
  return &m_userListModel;
}

QString
IRCChannelProxy::channelName ()
{
  return m_channelName;
}

void
IRCChannelProxy::sendMessage (const QString& message)
{
  QStringList arguments;
  arguments << m_channelName;
  arguments << message;
  m_clientInterface->sendIRCCommand (IRCCommand::PrivateMessage, arguments);
}

void
IRCChannelProxy::sendJoinRequest ()
{
  m_clientInterface->sendIRCCommand (IRCCommand::Join, QStringList (m_channelName));
}


void
IRCChannelProxy::leave (const QString& reason)
{
  Q_UNUSED (reason);
}

IRCClientImpl::IRCClientImpl (QObject *parent)
  : IRCClientInterface (parent)
{
  connect (&m_tcpSocket, SIGNAL (connected ()), this, SLOT (handleConnected ()));
  connect (&m_tcpSocket, SIGNAL (disconnected ()), this, SLOT (handleDisconnected ()));
  connect (&m_tcpSocket, SIGNAL (readyRead ()), this, SLOT (handleReadyRead ()));
}

IRCClientImpl::~IRCClientImpl ()
{
  foreach (IRCChannelProxyInterface *ircChannelProxy, m_channels)
    {
      delete ircChannelProxy;
    }
}

void
IRCClientImpl::connectToHost (const QHostAddress& host, int port, const QString& initialNick)
{
  m_host = host;
  m_nickname = initialNick;
  m_tcpSocket.connectToHost(host, port);
}

void
IRCClientImpl::disconnect ()
{
  m_tcpSocket.disconnect ();
}

void
IRCClientImpl::reconnect ()
{
  disconnect ();
  connectToHost (m_host, m_port, m_nickname);
}

bool
IRCClientImpl::isConnected ()
{
  return m_connected;
}

const QHostAddress&
IRCClientImpl::host()
{
  return m_host;
}

int
IRCClientImpl::port()
{
  return m_port;
}

IRCChannelProxyInterface *
IRCClientImpl::ircChannelProxy (const QString &channel)
{
  if (!m_channels.contains (channel))
      m_channels[channel] = new IRCChannelProxy(this, channel);
  return m_channels[channel];
}

void
IRCClientImpl::sendNicknameChangeRequest (const QString &nickname)
{
  sendIRCCommand (IRCCommand::Nick, QStringList (nickname));
}

void
IRCClientImpl::sendPrivateMessage (const QString &recipient, const QString &message)
{
  QStringList arguments;
  arguments << recipient;
  arguments << message;
  sendIRCCommand (IRCCommand::PrivateMessage, arguments);
}

const QString&
IRCClientImpl::nickname ()
{
  return m_nickname;
}

void
IRCClientImpl::handleConnected ()
{
  m_connected = true;
  QStringList arguments;
  arguments << "na" << "0" << "0" << "na";
  sendIRCCommand (IRCCommand::User, arguments);
  sendNicknameChangeRequest (m_nickname);
  emit connected (m_host.toString ());
}

void
IRCClientImpl::handleDisconnected ()
{
  m_connected = false;
  emit disconnected ();
}

void
IRCClientImpl::handleReadyRead ()
{
  QByteArray line;
  do
    {
      line = m_tcpSocket.readLine();
      if (line.size ())
        handleIncomingLine(QString(line));
      else
        break;
    }
  while (true);
}

void
IRCClientImpl::handleNicknameChanged (const QString &oldNick, const QString &newNick)
{
  emit nicknameChanged (oldNick, newNick);
}

void
IRCClientImpl::handleUserJoined (const QString &nick, const QString &channel)
{
  emit userJoined (nick, channel);
}

void
IRCClientImpl::handleUserQuit (const QString &nick, const QString &reason)
{
  emit userQuit (nick, reason);
}

void
IRCClientImpl::handleIncomingLine (const QString &line)
{
  emit debugMessage (QString (">>>recv: \"%1\"").arg (line));
  if (m_connected && !line.isEmpty())
    {
      IRCServerMessage ircEvent(line);
      if (ircEvent.isNumericValue () == true)
        {
          switch (ircEvent.numericValue ())
            {
              case IRCReply::Welcome:
                emit loggedIn (nickname ());
                break;
              case IRCError::NicknameInUse:
              case IRCError::NickCollision:
                emit debugMessage ("FIXME: Received nickname in use reply.");
                break;
              case IRCError::PasswordMismatch:
                emit debugMessage ("FIXME: Received password mismatch reply.");
                break;
              case IRCReply::MessageOfTheDayStart:
              case IRCReply::MessageOfTheDay:
              case IRCReply::MessageOfTheDayEnd:
              case IRCError::NoMessageOfTheDay:
                break;
              case IRCReply::NoTopic:
              case IRCReply::Topic:
                break;
              case IRCReply::NameReply:
                emit debugMessage (QString ("LINKME: (NameReply) \'%1\'").arg (ircEvent.parameter (3)));
                //m_nickList = event->getParam (3).split (QRegExp ("\\s+"), QString::SkipEmptyParts);
                break;
            }
        }
      else
        {
          QString command = ircEvent.command ();
          if (command == IRCCommand::Nick)
            {
              handleNicknameChanged (ircEvent.parameter (0), ircEvent.parameter (1));
            }
          else if (command == IRCCommand::Quit)
            {
              handleUserQuit (ircEvent.nick (), ircEvent.parameter (0));
            }
          else if (command == IRCCommand::Join)
            {
              handleUserJoined(ircEvent.nick (), ircEvent.parameter (0));
            }
          else if (command == IRCCommand::Part)
            {
              emit debugMessage ("WRITEME: Received part.");
              //emit part (ircEvent.getNick ().toStdString ().c_str (),
              //           ircEvent.getParam (0).toStdString ().c_str (),
              //           ircEvent.getParam (1).toStdString ().c_str ());
            }
          else if (command == IRCCommand::Mode)
            {
              emit debugMessage ("WRITEME: Received mode.");
              //emit mode (&ircEvent);
            }
          else if (command == IRCCommand::Topic)
            {
              emit debugMessage ("WRITEME: Received topic.");
              //emit topic (ircEvent.getNick ().toStdString ().c_str (),
              //            ircEvent.getParam (0).toStdString ().c_str (),
              //            ircEvent.getParam (1).toStdString ().c_str ());
            }
          else if (command == IRCCommand::Kick)
            {
              emit debugMessage ("WRITEME: Received kick.");
              //emit kick (ircEvent.getNick ().toStdString ().c_str (),
              //           ircEvent.getParam (0).toStdString ().c_str (),
              //           ircEvent.getParam (1).toStdString ().c_str (),
              //           ircEvent.getParam (2).toStdString ().c_str ());
            }
          else if (command == IRCCommand::Invite)
            {
              emit debugMessage ("WRITEME: Received invite.");
              //emit invite (ircEvent.getNick ().toStdString ().c_str (),
              //             ircEvent.getParam (1).toStdString ().c_str ());
            }
          else if (command == IRCCommand::PrivateMessage)
            {
              emit message (ircEvent.parameter (0), ircEvent.nick (), ircEvent.parameter (1));
            }
          else if (command == IRCCommand::Notice)
            {
              emit notification (ircEvent.nick ().toStdString ().c_str (),
                                 ircEvent.parameter (1).toStdString ().c_str ());
            }
          else if (command == IRCCommand::Ping)
            {
              sendIRCCommand (IRCCommand::Pong, QStringList (m_nickname));
            }
          else if (command == IRCCommand::Error)
            {
              emit error (ircEvent.parameter (0));
            }
          else
            {
              emit debugMessage (QString("FIXME: Received unknown reply: %1").arg(command));
              // not recognized.
            }
        }
    }
}

void
IRCClientImpl::sendLine (const QString &line)
{
  emit debugMessage (QString (">>>send: \"%1\"").arg (line));
  if (m_connected)
    m_tcpSocket.write ((line + "\r\n").toStdString ().c_str ());
}

void
IRCClientImpl::sendIRCCommand (const QString &command, const QStringList &arguments)
{
  QString line = command;
  for (int i = 0; i < arguments.size (); i++)
    {
      bool applyColon = false;
      // Usually all parameters are separated by spaces.
      // The last parameter of the message may contain spaces, it is usually used
      // to transmit messages. In order to parse it correctly, if needs to be prefixed
      // with a colon, so the server knows to ignore all forthcoming spaces and has to treat
      // all remaining characters as a single parameter. If we detect any whitespace in the
      // last argument, prefix it with a colon:
      if ((i == arguments.size () - 1) && arguments.at (i).contains (QRegExp("\\s")))
        applyColon = true;
      line += QString (" %1%2").arg (applyColon ? ":" : "").arg (arguments.at (i));
    }
  sendLine (line);
}