You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1725 lines
50 KiB

11 years ago
/**
* Copyright (C) 2013-2017 Jessica James.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Written by Jessica James <jessica.aj@outlook.com>
11 years ago
*/
#include <cstring>
#include <cstdio>
#include <ctime>
#include "Jupiter.h"
#include "Functions.h"
#include "IRC_Client.h"
#include "TCPSocket.h"
#include "CString.h"
#include "String.hpp"
11 years ago
#include "Plugin.h"
#include "Base64.h"
//#define SHORT_IRC_MACROS
#include "IRC_Numerics.h"
#if defined _WIN32
#include <WinSock2.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
using namespace Jupiter::literals;
Jupiter::IRC::Client::Client(Jupiter::Config *in_primary_section, Jupiter::Config *in_secondary_section)
11 years ago
{
m_primary_section = in_primary_section;
m_secondary_section = in_secondary_section;
if (m_primary_section != nullptr)
m_primary_section_name = m_primary_section->getName();
m_server_hostname = Jupiter::IRC::Client::readConfigValue("Hostname"_jrs, "irc.cncirc.net"_jrs);
11 years ago
m_log_file_name = Jupiter::IRC::Client::readConfigValue("LogFile"_jrs);
m_nickname = Jupiter::IRC::Client::readConfigValue("Nick"_jrs, "Jupiter"_jrs);
11 years ago
m_realname = Jupiter::IRC::Client::readConfigValue("RealName"_jrs, "Jupiter IRC Client"_jrs);
11 years ago
m_sasl_password = Jupiter::IRC::Client::readConfigValue("SASL.Password"_jrs);
if (m_sasl_password.isEmpty())
m_sasl_password = Jupiter::IRC::Client::readConfigValue("SASL.Pass"_jrs);
11 years ago
m_sasl_account = Jupiter::IRC::Client::readConfigValue("SASL.Account"_jrs);
if (m_sasl_account.isEmpty())
m_sasl_account = m_nickname;
11 years ago
m_auto_part_message = Jupiter::IRC::Client::readConfigValue("AutoPartMessage"_jrs);
11 years ago
m_ssl = Jupiter::IRC::Client::readConfigBool("SSL"_jrs);
m_ssl_certificate = Jupiter::IRC::Client::readConfigValue("Certificate"_jrs);
m_ssl_key = Jupiter::IRC::Client::readConfigValue("Key"_jrs);
if (m_ssl_certificate.isEmpty())
11 years ago
{
m_ssl_certificate = Jupiter::IRC::Client::readConfigValue("Cert"_jrs);
if (m_ssl_certificate.isEmpty())
m_ssl_certificate = m_ssl_key;
11 years ago
}
if (m_ssl_key.isEmpty())
m_ssl_key = m_ssl_certificate;
11 years ago
m_join_on_kick = Jupiter::IRC::Client::readConfigBool("AutoJoinOnKick"_jrs);
m_reconnect_delay = Jupiter::IRC::Client::readConfigInt("AutoReconnectDelay"_jrs);
m_max_reconnect_attempts = Jupiter::IRC::Client::readConfigInt("MaxReconnectAttempts"_jrs);
m_server_port = (unsigned short)Jupiter::IRC::Client::readConfigInt("Port"_jrs, m_ssl ? 994 : 194);
m_default_chan_type = Jupiter::IRC::Client::readConfigInt("Channel.Type"_jrs);
11 years ago
if (Jupiter::IRC::Client::readConfigBool("PrintOutput"_jrs, true))
m_output = stdout;
else
m_output = nullptr;
if (m_log_file_name.isNotEmpty())
m_log_file = fopen(m_log_file_name.c_str(), "a+b");
else m_log_file = nullptr;
11 years ago
if (m_ssl)
11 years ago
{
Jupiter::SecureTCPSocket *t = new Jupiter::SecureTCPSocket();
if (m_ssl_certificate.isNotEmpty())
t->setCertificate(m_ssl_certificate, m_ssl_key);
m_socket = t;
11 years ago
}
else m_socket = new Jupiter::TCPSocket();
11 years ago
m_connection_status = 0;
11 years ago
}
Jupiter::IRC::Client::~Client()
{
m_socket->close();
11 years ago
if (m_socket != nullptr)
delete m_socket;
11 years ago
if (m_log_file != nullptr)
fclose(m_log_file);
11 years ago
}
void Jupiter::IRC::Client::OnConnect()
{
return;
}
void Jupiter::IRC::Client::OnDisconnect()
{
return;
}
void Jupiter::IRC::Client::OnReconnectAttempt(bool)
{
return;
}
void Jupiter::IRC::Client::OnRaw(const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnNumeric(long int, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnError(const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnChat(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnNotice(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnServerNotice(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnCTCP(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnAction(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnInvite(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnJoin(const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnPart(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnNick(const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnKick(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnQuit(const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
void Jupiter::IRC::Client::OnMode(const Jupiter::ReadableString &, const Jupiter::ReadableString &, const Jupiter::ReadableString &)
11 years ago
{
return;
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getConfigSection() const
11 years ago
{
if (m_primary_section != nullptr)
return m_primary_section_name;
return Jupiter::ReferenceString::empty;
}
Jupiter::Config *Jupiter::IRC::Client::getPrimaryConfigSection() const
{
return m_primary_section;
}
Jupiter::Config *Jupiter::IRC::Client::getSecondaryConfigSection() const
{
return m_secondary_section;
}
void Jupiter::IRC::Client::setPrimaryConfigSection(Jupiter::Config *in_primary_section)
{
m_primary_section = in_primary_section;
if (m_primary_section != nullptr)
m_primary_section_name = m_primary_section->getName();
else
m_primary_section_name.erase();
}
void Jupiter::IRC::Client::setSecondaryConfigSection(Jupiter::Config *in_secondary_section)
{
m_secondary_section = in_secondary_section;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getLogFile() const
11 years ago
{
return m_log_file_name;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getPrefixes() const
11 years ago
{
return m_prefixes;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getPrefixModes() const
{
return m_prefix_modes;
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getNickname() const
11 years ago
{
return m_nickname;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getRealname() const
11 years ago
{
return m_realname;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getServerName() const
11 years ago
{
return m_server_name;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::getServerHostname() const
11 years ago
{
return m_server_hostname;
11 years ago
}
unsigned short Jupiter::IRC::Client::getServerPort() const
{
return m_server_port;
11 years ago
}
time_t Jupiter::IRC::Client::getReconnectDelay() const
{
return m_reconnect_delay;
11 years ago
}
time_t Jupiter::IRC::Client::getReconnectTime() const
{
return m_reconnect_time;
11 years ago
}
int Jupiter::IRC::Client::getReconnectAttempts() const
{
return m_reconnect_attempts;
11 years ago
}
int Jupiter::IRC::Client::getMaxReconnectAttempts() const
{
return m_max_reconnect_attempts;
11 years ago
}
int Jupiter::IRC::Client::getDefaultChanType() const
11 years ago
{
return m_default_chan_type;
11 years ago
}
FILE *Jupiter::IRC::Client::getPrintOutput() const
{
return m_output;
11 years ago
}
void Jupiter::IRC::Client::setPrintOutput(FILE *f)
{
m_output = f;
11 years ago
}
inline Jupiter::ReferenceString getSender(const Jupiter::ReadableString &line)
11 years ago
{
return Jupiter::ReferenceString::getWord(line, 0, ":! ");
11 years ago
}
int Jupiter::IRC::Client::getAccessLevel(const Channel &in_channel, const Jupiter::ReadableString &in_nickname) const
{
char prefix = in_channel.getUserPrefix(in_nickname);
if (prefix != 0)
return static_cast<int>(m_prefixes.size() - m_prefixes.find(prefix));
return 0;
}
int Jupiter::IRC::Client::getAccessLevel(const Jupiter::ReadableString &in_channel, const Jupiter::ReadableString &in_nickname) const
11 years ago
{
Jupiter::IRC::Client::Channel *channel = m_channels.get(in_channel);
if (channel != nullptr)
return this->getAccessLevel(*channel, in_nickname);
11 years ago
return 0;
}
void Jupiter::IRC::Client::send(const Jupiter::ReadableString &rawMessage)
11 years ago
{
Jupiter::StringS out = rawMessage;
out += ENDL;
11 years ago
m_socket->send(out);
11 years ago
}
const Jupiter::IRC::Client::UserTableType &Jupiter::IRC::Client::getUsers() const
11 years ago
{
return m_users;
11 years ago
}
size_t Jupiter::IRC::Client::getUserCount() const
11 years ago
{
return m_users.size();
11 years ago
}
Jupiter::IRC::Client::User *Jupiter::IRC::Client::getUser(const Jupiter::ReadableString &in_nickname) const
{
return m_users.get(in_nickname);
}
const Jupiter::IRC::Client::ChannelTableType &Jupiter::IRC::Client::getChannels() const
{
return m_channels;
}
size_t Jupiter::IRC::Client::getChannelCount() const
11 years ago
{
return m_channels.size();
11 years ago
}
Jupiter::IRC::Client::Channel *Jupiter::IRC::Client::getChannel(const Jupiter::ReadableString &in_channel) const
11 years ago
{
return m_channels.get(in_channel);
11 years ago
}
bool Jupiter::IRC::Client::isAutoReconnect() const
{
return m_max_reconnect_attempts != 0;
11 years ago
}
void Jupiter::IRC::Client::setAutoReconnect(int val)
{
m_max_reconnect_attempts = val;
11 years ago
}
void Jupiter::IRC::Client::joinChannel(const Jupiter::ReadableString &in_channel)
11 years ago
{
m_socket->send(Jupiter::StringS::Format("JOIN %.*s" ENDL, in_channel.size(), in_channel.ptr()));
11 years ago
}
void Jupiter::IRC::Client::joinChannel(const Jupiter::ReadableString &in_channel, const Jupiter::ReadableString &in_password)
11 years ago
{
m_socket->send(Jupiter::StringS::Format("JOIN %.*s %.*s" ENDL, in_channel.size(), in_channel.ptr(), in_password.size(), in_password.ptr()));
11 years ago
}
void Jupiter::IRC::Client::partChannel(const Jupiter::ReadableString &in_channel)
11 years ago
{
m_socket->send(Jupiter::StringS::Format("PART %.*s" ENDL, in_channel.size(), in_channel.ptr()));
m_channels.get(in_channel)->setType(-2);
11 years ago
}
void Jupiter::IRC::Client::partChannel(const Jupiter::ReadableString &in_channel, const Jupiter::ReadableString &in_message)
11 years ago
{
m_socket->send(Jupiter::StringS::Format("PART %.*s :%.*s" ENDL, in_channel.size(), in_channel.ptr(), in_message.size(), in_message.ptr()));
m_channels.get(in_channel)->setType(-2);
11 years ago
}
void Jupiter::IRC::Client::sendMessage(const Jupiter::ReadableString &dest, const Jupiter::ReadableString &message)
11 years ago
{
m_socket->send(Jupiter::StringS::Format("PRIVMSG %.*s :%.*s" ENDL, dest.size(), dest.ptr(), message.size(), message.ptr()));
11 years ago
}
void Jupiter::IRC::Client::sendNotice(const Jupiter::ReadableString &dest, const Jupiter::ReadableString &message)
11 years ago
{
m_socket->send(Jupiter::StringS::Format("NOTICE %.*s :%.*s" ENDL, dest.size(), dest.ptr(), message.size(), message.ptr()));
11 years ago
}
size_t Jupiter::IRC::Client::messageChannels(int type, const Jupiter::ReadableString &message)
11 years ago
{
auto message_channel_callback = [this, type, &message](ChannelTableType::Bucket::Entry &in_entry)
11 years ago
{
if (in_entry.value.getType() == type)
this->sendMessage(in_entry.value.getName(), message);
};
m_channels.callback(message_channel_callback);
return m_channels.size();
11 years ago
}
size_t Jupiter::IRC::Client::messageChannels(const Jupiter::ReadableString &message)
11 years ago
{
auto message_channel_callback = [this, &message](ChannelTableType::Bucket::Entry &in_entry)
11 years ago
{
this->sendMessage(in_entry.value.getName(), message);
};
m_channels.callback(message_channel_callback);
return m_channels.size();
11 years ago
}
int Jupiter::IRC::Client::process_line(const Jupiter::ReadableString &line)
11 years ago
{
if (line.isNotEmpty())
11 years ago
{
Jupiter::IRC::Client::writeToLogs(line);
if (m_output != nullptr)
line.println(m_output);
Jupiter::ReferenceString w1 = Jupiter::ReferenceString::getWord(line, 0, WHITESPACE);
if (w1.isNotEmpty())
11 years ago
{
Jupiter::ReferenceString w2 = Jupiter::ReferenceString::getWord(line, 1, WHITESPACE);
int numeric = w2.asInt(10);
if (w1[0] == ':') //Messages
11 years ago
{
if (w2.isNotEmpty())
11 years ago
{
switch (numeric) // Numerics that don't rely on a specific connectionStatus.
11 years ago
{
case Reply::BOUNCE: // 010
{
Jupiter::ReferenceString portToken = Jupiter::ReferenceString::getWord(line, 4, " ");
unsigned short port;
if (portToken[0] == '+') // This is most likely not used anywhere.
11 years ago
{
port = (unsigned short)portToken.asUnsignedInt(10);
if (m_ssl == false)
11 years ago
{
m_ssl = true;
delete m_socket;
m_socket = new Jupiter::SecureTCPSocket();
}
}
else
{
port = (unsigned short)portToken.asUnsignedInt(10);
if (m_ssl == true)
{
m_ssl = false;
delete m_socket;
m_socket = new Jupiter::TCPSocket();
}
}
if (port != 0) // Don't default -- could be non-compliant input.
{
m_server_hostname = Jupiter::ReferenceString::getWord(line, 3, WHITESPACE);
m_server_port = port;
puts("Reconnecting due to old bounce.");
this->reconnect();
}
else puts("Error: Failed to parse bounce token.");
}
break;
} // numeric switch
switch (m_connection_status)
{
case 1: // Socket established -- attempting STARTTLS
switch (numeric)
{
case Reply::BOUNCEOLD: // 005
if (line.matchi("*:Try server *, port *"))
11 years ago
{
Jupiter::ReferenceString portToken = Jupiter::ReferenceString::getWord(line, 6, " ");
unsigned short bouncePort = (unsigned short)Jupiter::ReferenceString::getWord(line, 6, " ").asInt(10);
if (portToken[0] == '+') // This is almost certainly not used anywhere.
11 years ago
{
bouncePort = (unsigned short)portToken.asInt(10);
if (m_ssl == false)
11 years ago
{
m_ssl = true;
delete m_socket;
m_socket = new Jupiter::SecureTCPSocket();
11 years ago
}
}
else
{
bouncePort = (unsigned short)portToken.asInt(10);
if (m_ssl == true)
11 years ago
{
m_ssl = false;
delete m_socket;
m_socket = new Jupiter::TCPSocket();
11 years ago
}
}
if (bouncePort != 0)
11 years ago
{
m_server_hostname = Jupiter::ReferenceString::getWord(line, 4, " ");
m_server_hostname.truncate(1); // trailing comma
m_server_port = bouncePort;
11 years ago
puts("Reconnecting due to old bounce.");
this->reconnect();
}
else puts("Error: Failed to parse old bounce token.");
11 years ago
}
break;
case Error::UNKNOWNCOMMAND: // 421
{
Jupiter::ReferenceString command = Jupiter::ReferenceString::getWord(line, 2, " ");
if (command.equalsi("STARTTLS")) // Server doesn't support STARTTLS
Client::startCAP();
}
break;
case Reply::STARTTLS: // 670
{
Jupiter::SecureTCPSocket *t = new Jupiter::SecureTCPSocket(std::move(*m_socket));
delete m_socket;
m_socket = t;
m_ssl = true;
// toggle blocking to prevent error
if (m_ssl_certificate.isNotEmpty())
t->setCertificate(m_ssl_certificate, m_ssl_key);
bool goodSSL;
if (t->getBlockingMode() == false)
{
t->setBlocking(true);
goodSSL = t->initSSL();
t->setBlocking(false);
}
else goodSSL = t->initSSL();
if (goodSSL)
Client::startCAP();
else
{
// Something went wrong. Kill the socket.
t->close();
}
}
break;
case Error::STARTTLS: // 691
Client::startCAP();
break;
default:
break;
} // numeric switch
break;
case 2: // Capability negotiation
switch (numeric)
{
case 0:
if (w2.equalsi("CAP"))
{
Jupiter::ReferenceString w4 = Jupiter::ReferenceString::getWord(line, 3, WHITESPACE);
if (w4.equals("LS"))
11 years ago
{
Jupiter::ReferenceString listParams = Jupiter::ReferenceString::gotoWord(line, 4, WHITESPACE);
if (listParams[0] == ':') listParams.shiftRight(1);
unsigned int len = listParams.wordCount(WHITESPACE);
Jupiter::ReferenceString curr;
Jupiter::StringL req = "CAP REQ :";
bool sasl = false;
for (unsigned int i = 0; i < len; i++)
11 years ago
{
curr = listParams.getWord(i, WHITESPACE);
if (curr.equalsi("multi-prefix")) req += "multi-prefix ";
else if (curr.equalsi("userhost-in-names")) req += "userhost-in-names ";
else if (curr.equalsi("sasl"))
11 years ago
{
if (m_sasl_password.isNotEmpty())
11 years ago
{
req += "sasl "_jrs;
sasl = true;
11 years ago
}
}
// else; // We don't know what this is!
11 years ago
}
if (req.size() > 9)
11 years ago
{
req -= 1; // Trim off the extra space byte.
req += ENDL;
m_socket->send(req);
if (sasl)
m_socket->send("AUTHENTICATE PLAIN"_jrs ENDL);
11 years ago
}
if (!sasl)
11 years ago
{
m_socket->send("CAP END"_jrs ENDL);
Client::registerClient();
11 years ago
}
}
}
break;
case Error::UNKNOWNCOMMAND: // 421
if (w2.equalsi("CAP")) // Server doesn't support CAP
{
Client::registerClient();
}
break;
default:
break;
} // numeric switch
break;
case 3: // Registration sent, but not verified.
{
bool erroneous_nickname = false;
switch (numeric)
{
// We'll take any of these 4, just in-case any of them are missing. In general, this will trigger on 001.
case Reply::MYINFO: // 004
m_server_name = Jupiter::ReferenceString::getWord(line, 3, " ");
case Reply::WELCOME: // 001
case Reply::YOURHOST: // 002
case Reply::CREATED: // 003
m_connection_status = 4;
break;
// You have a bad nickname! Try the alt.
//case Error::NONICKNAMEGIVEN: // 431 -- Not consistently usable due to lack of command field.
case Error::ERRONEOUSNICKNAME: // 432
erroneous_nickname = true;
case Error::NICKNAMEINUSE: // 433
case Error::NICKCOLLISION: // 436
case Error::BANNICKCHANGE: // 437 -- Note: This conflicts with another token.
const Jupiter::ReadableString &altNick = Jupiter::IRC::Client::readConfigValue("AltNick"_jrs);
const Jupiter::ReadableString &configNick = Jupiter::IRC::Client::readConfigValue("Nick"_jrs, "Jupiter"_jrs);
if (altNick.isNotEmpty() && m_nickname.equalsi(altNick)) // The alternate nick failed.
{
m_nickname = configNick;
m_nickname += "1";
m_socket->send("NICK "_jrs + m_nickname + ENDL);
}
else if (m_nickname.equalsi(configNick)) // The config nick failed
{
if (altNick.isEmpty())
{
if (erroneous_nickname)
break; // If this nick is invalid, adding numbers won't help.
m_nickname += '1';
}
else
m_nickname = altNick;
m_socket->send("NICK "_jrs + m_nickname + ENDL);
}
// Note: Add a series of contains() functions to String_Type.
else
11 years ago
{
if (erroneous_nickname == false) // If this nick is invalid, adding numbers won't help.
11 years ago
{
if (m_nickname.size() > configNick.size())
11 years ago
{
int n = Jupiter_strtoi_nospace_s(m_nickname.ptr() + configNick.size(), m_nickname.size() - configNick.size(), 10);
m_nickname.format("%.*s%d", configNick.size(), configNick.ptr(), n + 1);
m_socket->send("NICK "_jrs + m_nickname + ENDL);
11 years ago
}
else
11 years ago
{
// Something strange is going on -- did somebody rehash?
// This can be somewhat edgy -- this will only trigger if someone rehashes AND the new nickname is shorter.
// However, it won't be fatal even if the new nickname's length is >= the old.
m_nickname = configNick;
m_socket->send("NICK "_jrs + m_nickname + ENDL);
11 years ago
}
}
else
{
// Disconnect and don't try again.
// Consider passing this to plugins so that they can figure it out (i.e: a plugin could display a prompt and ask for input).
11 years ago
}
}
break;
}
}
break;
case 4: // Registration verified, but connection process in progress.
switch (numeric)
{
case Reply::ISUPPORT: // 005
{
size_t pos = line.find("PREFIX=("_jrs);
if (pos != Jupiter::INVALID_INDEX)
{
Jupiter::ReferenceString ref = Jupiter::ReferenceString::substring(line, pos + 8);
m_prefix_modes = Jupiter::ReferenceString::getWord(ref, 0, ")");
ref.shiftRight(ref.find(')') + 1);
m_prefixes = Jupiter::ReferenceString::getWord(ref, 0, " " ENDL);
}
pos = line.find("CHANMODES="_jrs);
if (pos != Jupiter::INVALID_INDEX)
{
Jupiter::ReferenceString ref = Jupiter::ReferenceString::substring(line, pos + 10);
ref = ref.getWord(0, " ");
size_t pos2 = ref.find(',', 0);
if (pos != INVALID_INDEX)
11 years ago
{
m_modeA = ref.getWord(0, ", ");
ref.shiftRight(pos + 1);
pos2 = ref.find(',', 0);
if (pos != INVALID_INDEX)
11 years ago
{
m_modeB = ref.getWord(0, ", ");
ref.shiftRight(pos + 1);
pos2 = ref.find(',', 0);
if (pos != INVALID_INDEX)
11 years ago
{
m_modeC = ref.getWord(0, ", ");
ref.shiftRight(pos + 1);
m_modeD = ref.getWord(0, ", ");
11 years ago
}
}
}
}
pos = line.find("CHANTYPES="_jrs);
if (pos != Jupiter::INVALID_INDEX)
{
Jupiter::ReferenceString ref = Jupiter::ReferenceString::substring(line, pos + 10);
m_chan_types = ref.getWord(0, " ");
}
}
break;
case Reply::LUSERCLIENT: // 251
{
Jupiter::StringL key = "RawData.";
size_t offset;
11 years ago
unsigned int i = 1;
Jupiter::ReferenceString value;
auto config_loop_condition = [&]
{
offset = key.aformat("%u", i);
value = Jupiter::IRC::Client::readConfigValue(key);
return !value.isEmpty();
};
while (config_loop_condition())
{
key.truncate(offset);
Jupiter::IRC::Client::send(value);
i++;
}
auto auto_join_channels_callback = [this](Jupiter::Config::SectionHashTable::Bucket::Entry &in_entry)
{
if (in_entry.value.get<bool>("AutoJoin"_jrs, false))
this->joinChannel(in_entry.value.getName());
};
Jupiter::Config *config = m_primary_section->getSection("Channels"_jrs);
if (config != nullptr)
config->getSections().callback(auto_join_channels_callback);
config = m_secondary_section->getSection("Channels"_jrs);
if (config != nullptr)
config->getSections().callback(auto_join_channels_callback);
11 years ago
m_connection_status = 5;
m_reconnect_attempts = 0;
this->OnConnect();
for (i = 0; i < Jupiter::plugins->size(); i++) Jupiter::plugins->get(i)->OnConnect(this);
}
break;
}
break;
default: // Post-registration.
if (w2.equalsi("PRIVMSG"))
{
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 2, WHITESPACE);
if (chan.isNotEmpty())
{
Jupiter::ReferenceString nick = getSender(line);
if (nick.isNotEmpty())
11 years ago
{
Jupiter::ReferenceString premessage = Jupiter::ReferenceString::substring(line, line.find(':', 1) + 1);
if (premessage[0] == '\001') //CTCP (ACTIONs are included)
11 years ago
{
Jupiter::ReferenceString rawmessage(premessage.ptr() + 1, premessage.size() - 1);
Jupiter::ReferenceString command = rawmessage.getWord(0, WHITESPACE);
if (command[command.size() - 1] == IRC::CTCP) command.truncate(1);
Jupiter::ReferenceString message = rawmessage.substring(rawmessage.find(' ') + 1, rawmessage.find(IRC::CTCP));
if (message[message.size() - 1] == IRC::CTCP) message.truncate(1);
if (command.equals("ACTION"))
11 years ago
{
this->OnAction(chan, nick, message);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnAction(this, chan, nick, message);
11 years ago
}
else
{
Jupiter::StringL response = "NOTICE ";
response += nick;
response += " :" IRCCTCP;
response += command;
response += ' ';
if (command.equals("PING")) response += message;
else if (command.equals("VERSION")) response += Jupiter::version;
else if (command.equals("FINGER")) response += "Oh, yeah, a little to the left.";
else if (command.equals("SOURCE")) response += "https://github.com/JAJames/Jupiter";
else if (command.equals("USERINFO")) response += "Hey, I'm Jupiter! If you have questions, ask Agent! (irc.cncirc.net)";
else if (command.equals("CLIENTINFO")) response += "I'll tell you what I don't know: This command!";
else if (command.equals("TIME")) response += getTime();
else if (command.equals("ERRMSG")) response += message;
else
11 years ago
{
response = "NOTICE ";
response += nick;
response += " :" IRCCTCP "ERRMSG ";
response += command;
response += " :Query is unknown";
11 years ago
}
response += IRCCTCP ENDL;
m_socket->send(response);
this->OnCTCP(chan, nick, command, message);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnCTCP(this, chan, nick, message);
11 years ago
}
}
else
11 years ago
{
Jupiter::ReferenceString message = premessage;
this->OnChat(chan, nick, message);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnChat(this, chan, nick, message);
11 years ago
}
}
}
}
else if (w2.equalsi("NOTICE"))
{
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 2, WHITESPACE);
if (chan.isNotEmpty())
{
size_t pos = line.find('!', 0);
auto message = Jupiter::ReferenceString::substring(line, line.find(':', 1) + 1, line.size());
if (pos < line.find(' '))
{
auto nick = Jupiter::ReferenceString::substring(line, 1, pos);
this->OnNotice(chan, nick, message);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnNotice(this, chan, nick, message);
11 years ago
}
else
11 years ago
{
auto sender = getSender(line);
if (sender.isNotEmpty())
11 years ago
{
this->OnServerNotice(chan, sender, message);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnServerNotice(this, chan, sender, message);
11 years ago
}
}
}
}
else if (w2.equalsi("NICK"))
{
auto nick = getSender(line);
Jupiter::ReferenceString newnick = Jupiter::ReferenceString::substring(line, line.find(' ', 1) + 1);
if (newnick.isNotEmpty() && newnick[0] == ':') newnick.shiftRight(1);
if (nick.equalsi(m_nickname))
{
m_nickname = newnick;
}
Jupiter::IRC::Client::User *user = Client::findUser(nick);
if (user != nullptr)
{
user->m_nickname = newnick;
this->OnNick(nick, newnick);
}
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnNick(this, nick, newnick);
}
else if (w2.equalsi("JOIN"))
{
auto nick = getSender(line);
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 2, WHITESPACE);
if (chan[0] == ':')
chan.shiftRight(1);
Channel *channel = m_channels.get(chan);
if (m_nickname.equalsi(nick))
{
// TODO: Optimize by simply wiping channel data, rather than removing and re-adding
if (channel != nullptr)
Client::delChannel(channel->getName());
Client::addChannel(chan);
channel = m_channels.get(chan);
channel->m_adding_names = true;
if (channel->getType() < 0)
11 years ago
{
if (m_auto_part_message.isNotEmpty())
Jupiter::IRC::Client::partChannel(chan, m_auto_part_message);
else
Jupiter::IRC::Client::partChannel(chan);
}
}
else if (channel != nullptr)
channel->addUser(Client::findUserOrAdd(nick));
this->OnJoin(chan, nick);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnJoin(this, chan, nick);
}
else if (w2.equalsi("PART"))
{
auto nick = getSender(line);
if (nick.isNotEmpty())
{
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 2, WHITESPACE);
if (chan.isNotEmpty())
{
Channel *channel = m_channels.get(chan);
if (channel != nullptr)
11 years ago
{
Jupiter::IRC::Client::User *user = m_users.get(nick);
if (user != nullptr)
11 years ago
{
channel->delUser(nick);
Jupiter::ReferenceString reason;
size_t pos = line.find(':', 1);
if (pos != Jupiter::INVALID_INDEX)
reason = Jupiter::ReferenceString::substring(line, pos + 1);
this->OnPart(chan, nick, reason);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnPart(this, chan, nick, reason);
if (nick.equalsi(m_nickname))
Client::delChannel(chan);
if (user->getChannelCount() == 0)
m_users.remove(nick);
11 years ago
}
}
}
}
}
else if (w2.equalsi("KICK"))
{
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 2, WHITESPACE);
if (chan.isNotEmpty())
{
Jupiter::ReferenceString kicker = getSender(line);
if (kicker.isNotEmpty())
11 years ago
{
Jupiter::ReferenceString kicked = Jupiter::ReferenceString::getWord(line, 3, WHITESPACE);
if (kicked.isNotEmpty())
11 years ago
{
Channel *channel = m_channels.get(chan);
if (channel != nullptr)
11 years ago
{
Jupiter::IRC::Client::User *user = m_users.get(kicked);
if (user != nullptr)
11 years ago
{
channel->delUser(kicked);
size_t pos = line.find(':', 1);
Jupiter::ReferenceString reason;
if (pos != Jupiter::INVALID_INDEX)
reason = Jupiter::ReferenceString::substring(line, pos + 1);
this->OnKick(chan, kicker, kicked, reason);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnKick(this, chan, kicker, kicked, reason);
if (kicked.equalsi(m_nickname))
11 years ago
{
Client::delChannel(chan);
if (m_join_on_kick)
Jupiter::IRC::Client::joinChannel(chan);
11 years ago
}
if (user->getChannelCount() == 0)
m_users.remove(kicked);
11 years ago
}
}
}
}
}
}
else if (w2.equalsi("QUIT"))
{
Jupiter::ReferenceString nick = getSender(line);
Jupiter::ReferenceString message = Jupiter::ReferenceString::substring(line, line.find(':', 1) + 1);
Jupiter::IRC::Client::User *user = m_users.get(nick);
if (user != nullptr)
{
auto remove_user_callback = [&nick](ChannelTableType::Bucket::Entry &in_entry)
{
in_entry.value.delUser(nick);
};
m_channels.callback(remove_user_callback);
this->OnQuit(nick, message);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnQuit(this, nick, message);
m_users.remove(nick);
}
}
else if (w2.equalsi("INVITE"))
{
Jupiter::ReferenceString inviter = getSender(line);
Jupiter::ReferenceString invited = Jupiter::ReferenceString::getWord(line, 2, WHITESPACE);
Jupiter::ReferenceString chan = Jupiter::ReferenceString::substring(line, line.find(':', 1) + 1);
this->OnInvite(chan, inviter, invited);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnInvite(this, chan, inviter, invited);
}
else if (w2.equalsi("MODE"))
{
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 2, WHITESPACE);
if (chan.isNotEmpty())
{
if (m_chan_types.contains(chan[0]))
11 years ago
{
auto nick = getSender(line);
if (nick.isNotEmpty())
11 years ago
{
Jupiter::ReferenceString modestring = Jupiter::ReferenceString::substring(line, line.find(' ', 2) + 1);
if (modestring.wordCount(" ") > 1)
11 years ago
{
Jupiter::ReferenceString modes = modestring.getWord(0, " ");
if (modes.isNotEmpty())
11 years ago
{
modestring.shiftRight(modestring.find(' ') + 1);
Jupiter::ReferenceString tword;
unsigned char g = 0;
char symb = 0;
for (uint8_t z = 0; z != modes.size(); z++)
11 years ago
{
if (modes[z] == '+' || modes[z] == '-')
symb = modes[z];
else if (m_prefix_modes.contains(modes[z])) // user prefix mode
11 years ago
{
tword = modestring.getWord(g, " ");
if (tword.isNotEmpty())
11 years ago
{
Jupiter::IRC::Client::Channel *channel = m_channels.get(chan);
if (channel != nullptr)
11 years ago
{
if (symb == '+')
channel->addUserPrefix(Jupiter::ReferenceString(tword), m_prefixes[m_prefix_modes.find(modes[z])]);
else
channel->delUserPrefix(Jupiter::ReferenceString(tword), m_prefixes[m_prefix_modes.find(modes[z])]);
11 years ago
}
}
g++;
11 years ago
}
else if (m_modeA.contains(modes[z])) // mode type A
g++;
else if (m_modeB.contains(modes[z])) // mode type B
g++;
else if (m_modeC.contains(modes[z]) && symb == '+') // mode type C (with parameter)
g++;
// else; // mode type D
11 years ago
}
}
}
this->OnMode(chan, nick, modestring);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnMode(this, chan, nick, modestring);
11 years ago
}
}
}
}
// else if ACCOUNT
// else if CHGHOST
else if (numeric == Reply::NAMREPLY) // Some names.
11 years ago
{
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 4, " ");
Jupiter::ReferenceString names = Jupiter::ReferenceString::substring(line, line.find(':', 1) + 1);
Channel *channel = m_channels.get(chan);
if (channel != nullptr)
11 years ago
{
if (channel->m_adding_names == false)
{
Client::delChannel(chan);
Client::addChannel(chan);
channel = Jupiter::IRC::Client::getChannel(chan);
channel->m_adding_names = true;
}
// addNamesToChannel can be cut/pasted here
Client::addNamesToChannel(*channel, names);
11 years ago
}
}
else if (numeric == Reply::ENDOFNAMES) // We're done here.
{
Jupiter::ReferenceString chan = Jupiter::ReferenceString::getWord(line, 3, " ");
Channel *channel = m_channels.get(chan);
if (channel != nullptr)
channel->m_adding_names = false;
}
break;
11 years ago
}
}
}
else
{
if (w1.equals("PING"))
{
m_socket->send(Jupiter::StringS::Format("PONG %.*s" ENDL, w2.size(), w2.ptr()));
}
else if (w1.equals("NICK"))
{
if (w2.isNotEmpty())
m_nickname = w2;
}
else if (w1.equals("ERROR"))
{
Jupiter::ReferenceString reason = Jupiter::ReferenceString::substring(line, line.find(':') + 1);
this->OnError(reason);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnError(this, reason);
Jupiter::IRC::Client::disconnect();
}
else if (w1.equals("AUTHENTICATE"))
{
if (m_sasl_password.isNotEmpty())
11 years ago
{
Jupiter::StringS auth_str = m_nickname + '\0' + m_sasl_account + '\0' + m_sasl_password;
char *enc = Jupiter::base64encode(auth_str.ptr(), auth_str.size());
m_socket->send("AUTHENTICATE "_jrs + enc + ENDL);
delete[] enc;
11 years ago
}
m_socket->send("CAP END" ENDL);
Client::registerClient();
11 years ago
}
}
if (numeric != 0)
{
this->OnNumeric(numeric, line);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnNumeric(this, numeric, line);
11 years ago
}
}
this->OnRaw(line);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnRaw(this, line);
11 years ago
}
return 0;
11 years ago
}
bool Jupiter::IRC::Client::connect()
{
const Jupiter::ReadableString &clientAddress = Jupiter::IRC::Client::readConfigValue("ClientAddress"_jrs);
if (m_socket->connect(m_server_hostname.c_str(), m_server_port, clientAddress.isEmpty() ? nullptr : Jupiter::CStringS(clientAddress).c_str(), (unsigned short)Jupiter::IRC::Client::readConfigLong("ClientPort"_jrs)) == false)
return false;
m_socket->setBlocking(false);
if (m_ssl == false && Jupiter::IRC::Client::readConfigBool("STARTTLS"_jrs, true))
11 years ago
{
m_socket->send("STARTTLS" ENDL);
m_connection_status = 1;
11 years ago
}
else
Client::startCAP();
return true;
11 years ago
}
void Jupiter::IRC::Client::disconnect(bool stayDead)
{
m_connection_status = 0;
m_socket->close();
m_reconnect_time = time(0) + m_reconnect_delay;
m_dead = stayDead;
11 years ago
this->OnDisconnect();
bool ssl = Jupiter::IRC::Client::readConfigBool("SSL"_jrs);
if (ssl != m_ssl)
{
m_ssl = ssl;
if (m_ssl)
{
Jupiter::SecureTCPSocket *t = new Jupiter::SecureTCPSocket(std::move(*m_socket));
if (m_ssl_certificate.isNotEmpty())
t->setCertificate(m_ssl_certificate, m_ssl_key);
delete m_socket;
m_socket = t;
}
else
{
Jupiter::TCPSocket *t = new Jupiter::TCPSocket(std::move(*m_socket));
delete m_socket;
m_socket = t;
}
}
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnDisconnect(this);
11 years ago
}
void Jupiter::IRC::Client::disconnect(const Jupiter::ReadableString &message, bool stayDead)
11 years ago
{
m_socket->send(Jupiter::StringS::Format("QUIT :%.*s" ENDL, message.size(), message.ptr()));
11 years ago
Jupiter::IRC::Client::disconnect(stayDead);
}
void Jupiter::IRC::Client::reconnect()
{
if (m_connection_status != 0) Jupiter::IRC::Client::disconnect();
m_reconnect_attempts++;
11 years ago
bool successConnect = Jupiter::IRC::Client::connect();
this->OnReconnectAttempt(successConnect);
for (size_t i = 0; i < Jupiter::plugins->size(); i++)
Jupiter::plugins->get(i)->OnReconnectAttempt(this, successConnect);
11 years ago
}
int Jupiter::IRC::Client::think()
{
auto handle_error = [this](int error_code)
{
if (this->m_dead == true)
return error_code;
if (this->m_max_reconnect_attempts < 0 || this->m_reconnect_attempts < this->m_max_reconnect_attempts)
{
if (!this->m_reconnect_delay || this->m_reconnect_time < time(0))
this->reconnect();
return 0;
}
return error_code;
};
if (m_connection_status == 0)
return handle_error(-1);
int tmp = m_socket->recv();
if (tmp > 0)
11 years ago
{
// Process incoming data
Jupiter::ReadableString::TokenizeResult<Jupiter::Reference_String> result = Jupiter::ReferenceString::tokenize(m_socket->getBuffer(), "\r\n"_jrs);
if (result.token_count != 0)
11 years ago
{
if (result.tokens[0].size() > 0)
{
// Ensure there's not a token getting split over separate buffers
if (m_last_line.size() > 0)
{
if (result.tokens[0][0] == '\n' && m_last_line[m_last_line.size() - 1] == '\r')
{
m_last_line.truncate(1); // Remove \r
Jupiter::IRC::Client::process_line(m_last_line);
m_last_line.erase();
result.tokens[0].shiftRight(1); // Remove \n
}
}
m_last_line += result.tokens[0];
}
if (result.token_count != 1)
{
Jupiter::IRC::Client::process_line(m_last_line);
m_last_line = result.tokens[result.token_count - 1];
for (size_t index = 1; index != result.token_count - 1; ++index)
if (Jupiter::IRC::Client::process_line(result.tokens[index]) != 0)
return handle_error(1);
}
11 years ago
}
return 0;
11 years ago
}
// No incoming data; check for errors
tmp = m_socket->getLastError();
if (tmp == 10035) // Operation would block
return 0;
// Serious error; disconnect if necessary
if (m_connection_status != 0)
Jupiter::IRC::Client::disconnect();
return handle_error(tmp);
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::readConfigValue(const Jupiter::ReadableString &key, const Jupiter::ReadableString &defaultValue) const
11 years ago
{
if (m_primary_section != nullptr)
{
const Jupiter::ReadableString &val = m_primary_section->get(key);
if (val.isNotEmpty())
return val;
}
if (m_secondary_section != nullptr)
return m_secondary_section->get(key, defaultValue);
return defaultValue;
11 years ago
}
bool Jupiter::IRC::Client::readConfigBool(const Jupiter::ReadableString &key, bool defaultValue) const
11 years ago
{
if (m_primary_section != nullptr)
{
const Jupiter::ReadableString &val = m_primary_section->get(key);
if (val.isNotEmpty())
return val.asBool();
}
if (m_secondary_section != nullptr)
return m_secondary_section->get<bool>(key, defaultValue);
return defaultValue;
11 years ago
}
int Jupiter::IRC::Client::readConfigInt(const Jupiter::ReadableString &key, int defaultValue) const
11 years ago
{
if (m_primary_section != nullptr)
{
const Jupiter::ReadableString &val = m_primary_section->get(key);
if (val.isNotEmpty())
return val.asInt();
}
if (m_secondary_section != nullptr)
return m_secondary_section->get<int>(key, defaultValue);
return defaultValue;
11 years ago
}
long Jupiter::IRC::Client::readConfigLong(const Jupiter::ReadableString &key, long defaultValue) const
11 years ago
{
if (m_primary_section != nullptr)
{
const Jupiter::ReadableString &val = m_primary_section->get(key);
if (val.isNotEmpty())
return val.asInt();
}
if (m_secondary_section != nullptr)
return m_secondary_section->get<long>(key, defaultValue);
return defaultValue;
11 years ago
}
double Jupiter::IRC::Client::readConfigDouble(const Jupiter::ReadableString &key, double defaultValue) const
11 years ago
{
if (m_primary_section != nullptr)
{
const Jupiter::ReadableString &val = m_primary_section->get(key);
if (val.isNotEmpty())
return val.asDouble();
}
if (m_secondary_section != nullptr)
return m_secondary_section->get<double>(key, defaultValue);
return defaultValue;
11 years ago
}
void Jupiter::IRC::Client::writeToLogs(const Jupiter::ReadableString &message)
11 years ago
{
if (m_log_file != nullptr)
11 years ago
{
message.println(m_log_file);
fflush(m_log_file);
11 years ago
}
}
/**
* @brief IRC Client Private Function Implementations
11 years ago
*/
void Jupiter::IRC::Client::delChannel(const Jupiter::ReadableString &in_channel)
11 years ago
{
m_channels.remove(in_channel);
11 years ago
}
Jupiter::IRC::Client::User *Jupiter::IRC::Client::findUser(const Jupiter::ReadableString &in_nickname) const
11 years ago
{
return m_users.get(in_nickname);
11 years ago
}
Jupiter::IRC::Client::User *Jupiter::IRC::Client::findUserOrAdd(const Jupiter::ReadableString &name)
11 years ago
{
Jupiter::ReferenceString nick = Jupiter::ReferenceString::getWord(name, 0, "!");
Jupiter::IRC::Client::User *result = Jupiter::IRC::Client::findUser(nick);
if (result != nullptr)
return result;
User user;
user.m_nickname = nick;
user.m_username = Jupiter::ReferenceString::getWord(name, 1, "!@");
user.m_hostname = Jupiter::ReferenceString::getWord(name, 2, "!@");
m_users.set(nick, user);
return m_users.get(nick);
11 years ago
}
void Jupiter::IRC::Client::addNamesToChannel(Channel &in_channel, Jupiter::ReadableString &in_names)
11 years ago
{
Jupiter::ReferenceString tmp;
11 years ago
size_t offset;
unsigned int nameLen = in_names.wordCount(" ");
for (size_t i = 0; i != nameLen; i++)
11 years ago
{
tmp = Jupiter::ReferenceString::getWord(in_names, i, " ");
if (tmp.isNotEmpty())
11 years ago
{
offset = tmp.span(m_prefixes);
tmp.shiftRight(offset);
Client::User *user = Client::findUserOrAdd(tmp);
tmp.shiftLeft(offset);
in_channel.addUser(user);
while (offset > 0)
11 years ago
{
--offset;
in_channel.addUserPrefix(user->getNickname(), tmp[offset]);
11 years ago
}
}
}
}
void Jupiter::IRC::Client::addChannel(const Jupiter::ReadableString &in_channel)
11 years ago
{
m_channels.set(in_channel, Channel(in_channel, this));
11 years ago
}
bool Jupiter::IRC::Client::startCAP()
11 years ago
{
m_connection_status = 2;
return m_socket->send("CAP LS"_jrs ENDL) > 0;
11 years ago
}
bool Jupiter::IRC::Client::registerClient()
11 years ago
{
bool result = true;
11 years ago
const char *localHostname = Jupiter::Socket::getLocalHostname();
Jupiter::StringS messageToSend;
messageToSend.format("USER %.*s %s %.*s :%.*s" ENDL, m_nickname.size(), m_nickname.ptr(), localHostname, m_server_hostname.size(), m_server_hostname.ptr(), m_realname.size(), m_realname.ptr());
if (m_socket->send(messageToSend) <= 0)
result = false;
messageToSend.format("NICK %.*s" ENDL, m_nickname.size(), m_nickname.ptr());
if (m_socket->send(messageToSend) <= 0)
result = false;
m_connection_status = 3;
return result;
11 years ago
}
/**
* User Implementation
*/
const Jupiter::ReadableString &Jupiter::IRC::Client::User::getNickname() const
11 years ago
{
return m_nickname;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::User::getUsername() const
11 years ago
{
return m_username;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::User::getHostname() const
11 years ago
{
return m_hostname;
11 years ago
}
size_t Jupiter::IRC::Client::User::getChannelCount() const
11 years ago
{
return m_channel_count;
11 years ago
}
/**
* Channel Implementation
*/
Jupiter::IRC::Client::Channel::Channel(const Jupiter::ReadableString &in_name, Jupiter::IRC::Client *in_parent)
11 years ago
{
auto to_lower = [&in_name]()
{
Jupiter::String result(in_name.size());
const char *itr = in_name.ptr();
const char *end = itr + in_name.size();
while (itr != end)
{
result += static_cast<char>(tolower(*itr));
++itr;
}
return result;
};
m_name = in_name;
m_parent = in_parent;
m_type = m_parent->getDefaultChanType();
Jupiter::String name = to_lower();
auto read_type = [&name](Jupiter::Config &in_config, int default_type)
{
return in_config["Channels"_jrs][name].get<int>("Type"_jrs, default_type);
};
if (m_parent->getSecondaryConfigSection() != nullptr)
m_type = read_type(*m_parent->getSecondaryConfigSection(), m_type);
if (m_parent->getPrimaryConfigSection() != nullptr)
m_type = read_type(*m_parent->getPrimaryConfigSection(), m_type);
11 years ago
}
Jupiter::IRC::Client::Channel::User *Jupiter::IRC::Client::Channel::addUser(Jupiter::IRC::Client::User *user)
11 years ago
{
Channel::User channel_user;
channel_user.m_user = user;
11 years ago
++user->m_channel_count;
11 years ago
m_users.set(channel_user.getNickname(), channel_user);
return m_users.get(channel_user.getNickname());
11 years ago
}
Jupiter::IRC::Client::Channel::User *Jupiter::IRC::Client::Channel::addUser(Jupiter::IRC::Client::User *user, const char prefix)
11 years ago
{
Channel::User channel_user;
channel_user.m_user = user;
channel_user.m_prefixes = prefix;
11 years ago
++user->m_channel_count;
11 years ago
m_users.set(channel_user.getNickname(), channel_user);
return m_users.get(channel_user.getNickname());
11 years ago
}
void Jupiter::IRC::Client::Channel::delUser(const Jupiter::ReadableString &in_nickname)
11 years ago
{
m_users.remove(in_nickname);
11 years ago
}
void Jupiter::IRC::Client::Channel::addUserPrefix(const Jupiter::ReadableString &in_nickname, char prefix)
11 years ago
{
Channel::User *user = m_users.get(in_nickname);
11 years ago
if (user != nullptr && user->m_prefixes.contains(prefix) == false)
user->m_prefixes += prefix;
11 years ago
}
void Jupiter::IRC::Client::Channel::delUserPrefix(const Jupiter::ReadableString &in_nickname, char prefix)
11 years ago
{
Channel::User *user = m_users.get(in_nickname);
11 years ago
if (user != nullptr)
user->m_prefixes.remove(prefix);
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::Channel::getName() const
11 years ago
{
return m_name;
11 years ago
}
Jupiter::IRC::Client::Channel::User *Jupiter::IRC::Client::Channel::getUser(const Jupiter::ReadableString &in_nickname) const
11 years ago
{
return m_users.get(in_nickname);
11 years ago
}
char Jupiter::IRC::Client::Channel::getUserPrefix(const Channel::User &in_user) const
{
const Jupiter::ReadableString &prefixes = m_parent->getPrefixes();
for (size_t index = 0; index != prefixes.size(); ++index)
if (in_user.m_prefixes.contains(prefixes[index]))
return prefixes[index];
return 0;
}
char Jupiter::IRC::Client::Channel::getUserPrefix(const Jupiter::ReadableString &in_nickname) const
11 years ago
{
Channel::User *user = m_users.get(in_nickname);
11 years ago
if (user != nullptr)
return this->getUserPrefix(*user);
11 years ago
return 0;
}
const Jupiter::IRC::Client::Channel::UserTableType &Jupiter::IRC::Client::Channel::getUsers() const
{
return m_users;
}
size_t Jupiter::IRC::Client::Channel::getUserCount() const
11 years ago
{
return m_users.size();
11 years ago
}
int Jupiter::IRC::Client::Channel::getType() const
{
return m_type;
11 years ago
}
void Jupiter::IRC::Client::Channel::setType(int in_type)
11 years ago
{
m_type = in_type;
11 years ago
}
/**
* Channel User Implementation
*/
Jupiter::IRC::Client::Channel::User::~User()
{
if (m_user != nullptr)
--m_user->m_channel_count;
11 years ago
}
Jupiter::IRC::Client::User *Jupiter::IRC::Client::Channel::User::getUser() const
{
return m_user;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::Channel::User::getPrefixes() const
11 years ago
{
return m_prefixes;
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::Channel::User::getNickname() const
11 years ago
{
return m_user->getNickname();
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::Channel::User::getUsername() const
11 years ago
{
return m_user->getUsername();
11 years ago
}
const Jupiter::ReadableString &Jupiter::IRC::Client::Channel::User::getHostname() const
11 years ago
{
return m_user->getHostname();
11 years ago
}
size_t Jupiter::IRC::Client::Channel::User::getChannelCount() const
11 years ago
{
return m_user->getChannelCount();
11 years ago
}