// ------------------------------------------------------------------------------------------------ #include "Session.hpp" // ------------------------------------------------------------------------------------------------ #include #include // ------------------------------------------------------------------------------------------------ namespace SqMod { // ------------------------------------------------------------------------------------------------ typedef int (*SendIrcMessageFunc)(irc_session_t *, const char *, const char *); // ------------------------------------------------------------------------------------------------ irc_callbacks_t Session::s_Callbacks; // ------------------------------------------------------------------------------------------------ bool Session::s_Initialized = false; // ------------------------------------------------------------------------------------------------ Session* Session::s_Session = nullptr; // ------------------------------------------------------------------------------------------------ Session::Sessions Session::s_Sessions; // ------------------------------------------------------------------------------------------------ SQInteger Session::Typename(HSQUIRRELVM vm) { static const SQChar name[] = _SC("SqIrcSession"); sq_pushstring(vm, name, sizeof(name)); return 1; } // ------------------------------------------------------------------------------------------------ void Session::Process() { // Do we only have one IRC session? if (s_Session) { s_Session->Update(); } // Do we have multiple sessions? else if (!s_Sessions.empty()) { for (Sessions::iterator itr = s_Sessions.begin(); itr != s_Sessions.end(); ++itr) { (*itr)->Update(); } } } // ------------------------------------------------------------------------------------------------ void Session::Terminate() { // Do we only have one IRC session? if (s_Session) { s_Session->Destroy(); /* This should do the job. */ } // Do we have multiple sessions? else if (!s_Sessions.empty()) { for (Sessions::iterator itr = s_Sessions.begin(); itr != s_Sessions.end(); ++itr) { (*itr)->Destroy(); } } } // ------------------------------------------------------------------------------------------------ irc_callbacks_t * Session::GetCallbacks() { // See if the callbacks structure was initialized before if (!s_Initialized) { // Initialize the callbacks s_Callbacks.event_connect = &OnConnect; s_Callbacks.event_nick = &OnNick; s_Callbacks.event_quit = &OnQuit; s_Callbacks.event_join = &OnJoin; s_Callbacks.event_part = &OnPart; s_Callbacks.event_mode = &OnMode; s_Callbacks.event_umode = &OnUmode; s_Callbacks.event_topic = &OnTopic; s_Callbacks.event_kick = &OnKick; s_Callbacks.event_channel = &OnChannel; s_Callbacks.event_privmsg = &OnPrivMsg; s_Callbacks.event_notice = &OnNotice; s_Callbacks.event_channel_notice = &OnChannelNotice; s_Callbacks.event_invite = &OnInvite; s_Callbacks.event_ctcp_req = &OnCtcpReq; s_Callbacks.event_ctcp_rep = &OnCtcpRep; s_Callbacks.event_ctcp_action = &OnCtcpAction; s_Callbacks.event_unknown = &OnUnknown; s_Callbacks.event_numeric = &OnNumeric; s_Callbacks.event_dcc_chat_req = &OnDccChatReq; s_Callbacks.event_dcc_send_req = &OnDccSendReq; // Specify that the callbacks were initialized s_Initialized = true; } // Return the callbacks structure return &s_Callbacks; } // ------------------------------------------------------------------------------------------------ void Session::Update() { // Make sure we even have a session if (!m_Session) { return; } // Make sure that the IRC session is connected else if (!irc_is_connected(m_Session)) { // Do we meet the condition to attempt to reconnect? if (m_Reconnect && (m_LeftTries != 0) && (m_NextTry <= SqMod_GetEpochTimeMicro())) { // Take out one try --m_LeftTries; // Update the time-point for the next try m_NextTry = (SqMod_GetEpochTimeMicro() + (m_Wait * 1000LL)); // Attempt to reconnect if (m_IPv6) { m_LastCode = irc_connect6(m_Session, m_Server.c_str(), m_Port, m_Passwd.empty() ? nullptr : m_Passwd.c_str(), m_Nick.c_str(), m_User.empty() ? nullptr : m_User.c_str(), m_Name.empty() ? nullptr : m_Name.c_str() ); } else { m_LastCode = irc_connect(m_Session, m_Server.c_str(), m_Port, m_Passwd.empty() ? nullptr : m_Passwd.c_str(), m_Nick.c_str(), m_User.empty() ? nullptr : m_User.c_str(), m_Name.empty() ? nullptr : m_Name.c_str() ); } } // We're done for now return; } // Create the structures for select() struct timeval tv; fd_set in_set, out_set; int maxfd = 0; // Pool for the specified amount of time tv.tv_usec = (m_PoolTime * 1000L); tv.tv_sec = 0; // Initialize the sets std::memset(&in_set, 0, sizeof(fd_set)); std::memset(&out_set, 0, sizeof(fd_set)); // Add the IRC session descriptors irc_add_select_descriptors(m_Session, &in_set, &out_set, &maxfd); // Call select() if (select(maxfd + 1, &in_set, &out_set, 0, &tv) < 0) { SqMod_LogErr("Unable to select() on IRC session"); } // Call irc_process_select_descriptors() for the session if (irc_process_select_descriptors (m_Session, &in_set, &out_set)) { // @TODO: The connection failed, or the server disconnected. Handle it! SqMod_LogWrn("The IRC connection failed, or the server disconnected."); } } // ------------------------------------------------------------------------------------------------ void Session::Release() { // Release the reference to the specified callbacks m_OnConnect.ReleaseGently(); m_OnNick.ReleaseGently(); m_OnQuit.ReleaseGently(); m_OnJoin.ReleaseGently(); m_OnPart.ReleaseGently(); m_OnMode.ReleaseGently(); m_OnUmode.ReleaseGently(); m_OnTopic.ReleaseGently(); m_OnKick.ReleaseGently(); m_OnChannel.ReleaseGently(); m_OnPrivMsg.ReleaseGently(); m_OnNotice.ReleaseGently(); m_OnChannelNotice.ReleaseGently(); m_OnInvite.ReleaseGently(); m_OnCtcpReq.ReleaseGently(); m_OnCtcpRep.ReleaseGently(); m_OnCtcpAction.ReleaseGently(); m_OnUnknown.ReleaseGently(); m_OnNumeric.ReleaseGently(); m_OnDccChatReq.ReleaseGently(); m_OnDccSendReq.ReleaseGently(); // Release the reference to the specified user data m_Data.Release(); } // ------------------------------------------------------------------------------------------------ void Session::Destroy() { // Make sure there's even a session to release if (!m_Session) { return; } // Disconnect the session Disconnect(); // Break the association with this instance (paranoia) irc_set_ctx(m_Session, nullptr); // Destroy the IRC session structure irc_destroy_session(m_Session); // Explicitly make sure no further calls can be made to this session (again... paranoia) m_Session = nullptr; // Release resources Release(); } // ------------------------------------------------------------------------------------------------ void Session::Validate() const { // Do we have a valid session handle? if (!m_Session) { STHROWF("Invalid IRC session"); } } // ------------------------------------------------------------------------------------------------ void Session::ValidateConnection() const { // Do we have a valid session handle? if (!m_Session) { STHROWF("Invalid IRC session"); } // Is the session connected? else if (!irc_is_connected(m_Session)) { STHROWF("Session is not connected"); } } // ------------------------------------------------------------------------------------------------ void Session::IsNotConnected() const { // Do we have a session that is not connected or trying to connect? if (m_Session && (irc_is_connected(m_Session) || m_Reconnect)) { STHROWF("Already connected or trying connect to IRC server"); } } // ------------------------------------------------------------------------------------------------ bool Session::ValidateEventSession(Session * ptr) { // Is the session instance valid? if (ptr) { return true; } // We can't throw an error here so we simply log it SqMod_LogErr("Cannot forward IRC event without a session instance"); // Invalid session instance return false; } // ------------------------------------------------------------------------------------------------ Session::Session() : m_Session(irc_create_session(GetCallbacks())) , m_Server(_SC("")) , m_Passwd(_SC("")) , m_Nick(_SC("")) , m_User(_SC("")) , m_Name(_SC("")) , m_Port(6667) , m_LastCode(0) , m_PoolTime(4) , m_Tries(3) , m_Wait(5000) , m_LeftTries(0) , m_NextTry(0) , m_SessionTime(0) , m_Reconnect(false) , m_IPv6(false) { if (!m_Session) { // Explicitly make sure no further calls can be made to this session m_Session = nullptr; // Now it's safe to throw the error STHROWF("Unable to create an IRC session"); } else { // Associate this wrapper instance with the session structure irc_set_ctx(m_Session, this); // Is this the only session instance? if (!s_Session && s_Sessions.empty()) { s_Session = this; } // Is this the second session instance? else if (s_Sessions.empty()) { s_Sessions.push_back(s_Session); s_Session = nullptr; s_Sessions.push_back(this); } // This is part of multiple session instances else { s_Sessions.push_back(this); } } } // ------------------------------------------------------------------------------------------------ Session::~Session() { Destroy(); // Attempt to find our self in the session pool Sessions::iterator itr = std::find(s_Sessions.begin(), s_Sessions.end(), this); // Are we in the pool? if (itr != s_Sessions.end()) { s_Sessions.erase(itr); /* Remove our self from the pool. */ } // Is there a single session and that's us? if (s_Session == this) { s_Session = nullptr; } } // ------------------------------------------------------------------------------------------------ Function & Session::GetEvent(Int32 evid) { // Identify the requested event type switch (evid) { case SET_CONNECT: return m_OnConnect; case SET_NICK: return m_OnNick; case SET_QUIT: return m_OnQuit; case SET_JOIN: return m_OnJoin; case SET_PART: return m_OnPart; case SET_MODE: return m_OnMode; case SET_UMODE: return m_OnUmode; case SET_TOPIC: return m_OnTopic; case SET_KICK: return m_OnKick; case SET_CHANNEL: return m_OnChannel; case SET_PRIVMSG: return m_OnPrivMsg; case SET_NOTICE: return m_OnNotice; case SET_CHANNELNOTICE: return m_OnChannelNotice; case SET_INVITE: return m_OnInvite; case SET_CTCPREQ: return m_OnCtcpReq; case SET_CTCPREP: return m_OnCtcpRep; case SET_CTCPACTION: return m_OnCtcpAction; case SET_UNKNOWN: return m_OnUnknown; case SET_NUMERIC: return m_OnNumeric; case SET_DCCCHATREQ: return m_OnDccChatReq; case SET_DCCSENDREQ: return m_OnDccSendReq; default: break; } // Default to a null function return NullFunction(); } // ------------------------------------------------------------------------------------------------ void Session::BindEvent(Int32 evid, Object & env, Function & func) { // Validate the handle Validate(); // Obtain the function instance called for this event Function & event = GetEvent(evid); // Is the specified callback function null? if (func.IsNull()) { event.ReleaseGently(); // Then release the current callback } // Does this function need a custom environment? else if (env.IsNull()) { event = func; } // Assign the specified environment and function else { event = Function(env.GetVM(), env, func.GetFunc()); } } // ------------------------------------------------------------------------------------------------ void Session::SetNick(CSStr nick) { // Validate the handle Validate(); // Validate the specified nick name if (!nick || *nick == '\0') { STHROWF("Invalid IRC nickname"); } // Do we have to issue a nickname command? else if (IsConnected()) { irc_cmd_nick(m_Session, nick); } // Simply save the specified nickname else { m_Nick.assign(nick); } } // ------------------------------------------------------------------------------------------------ void Session::SetPort(Uint32 num) { // The port cannot be changed once connected! IsNotConnected(); // Validate the specified port number if (num > 0xFFFF) { STHROWF("Port number is out of range: %u > %u", num, 0xFFFF); } // Assign the specified port number m_Port = num; } // ------------------------------------------------------------------------------------------------ Int32 Session::CmdNick(CSStr nick) { // Make sure the session is connected ValidateConnection(); // Validate the specified nick name if (!nick || strlen(nick) <= 0) { STHROWF("Invalid IRC nickname"); } // Issue the command and return the result return irc_cmd_nick(m_Session, nick); } // ------------------------------------------------------------------------------------------------ Object Session::GetNextTry() const { // Obtain the initial stack size const StackGuard sg; // Attempt to push a time-stamp instance on the stack SqMod_PushTimestamp(DefaultVM::Get(), m_NextTry); // Obtain the object from the stack and return it return Var< Object >(DefaultVM::Get(), -1).value; } // ------------------------------------------------------------------------------------------------ void Session::SetNextTry(Object & tm) { // Obtain the initial stack size const StackGuard sg; // Push the specified object onto the stack Var< Object >::push(DefaultVM::Get(), tm); // The resulted times-tamp value Int64 microseconds = 0; // Attempt to get the numeric value inside the specified object if (SQ_FAILED(SqMod_GetTimestamp(DefaultVM::Get(), -1, µseconds))) { STHROWF("Invalid time-stamp specified"); } // Assign the specified timestamp value m_NextTry = microseconds; } // ------------------------------------------------------------------------------------------------ Object Session::GetSessionTime() const { // Obtain the initial stack size const StackGuard sg; // Attempt to push a time-stamp instance on the stack if (m_SessionTime) { SqMod_PushTimestamp(DefaultVM::Get(), SqMod_GetEpochTimeMicro() - m_SessionTime); } // This session was not connected yet else { SqMod_PushTimestamp(DefaultVM::Get(), 0); } // Obtain the object from the stack and return it return Var< Object >(DefaultVM::Get(), -1).value; } // ------------------------------------------------------------------------------------------------ Int32 Session::Connect() { // Validate the handle Validate(); // Make sure we are allowed to connect IsNotConnected(); // Validate the specified server if (!m_Server.empty()) { STHROWF("Attempting to connect IRC without specifying a server"); } // Validate the specified nickname else if (!m_Nick.empty()) { STHROWF("Attempting to connect IRC without specifying a nickname"); } // Enable the reconnection system m_Reconnect = true; // Reset the number of tries m_LeftTries = m_Tries; // Set the time-point for the next try m_NextTry = (SqMod_GetEpochTimeMicro() + (m_Wait * 1000LL)); // This is not an IPv6 connection m_IPv6 = false; // Attempt to connect the session and return the result return irc_connect(m_Session, m_Server.c_str(), m_Port, m_Passwd.empty() ? nullptr : m_Passwd.c_str(), m_Nick.c_str(), m_User.empty() ? nullptr : m_User.c_str(), m_Name.empty() ? nullptr : m_Name.c_str() ); } // ------------------------------------------------------------------------------------------------ Int32 Session::Connect(CSStr server, Uint32 port, CSStr nick, CSStr passwd, CSStr user, CSStr name) { // Validate the handle Validate(); // Make sure we are allowed to connect IsNotConnected(); // Validate the specified port if (port > 0xFFFF) { STHROWF("Port number is out of range: %u > %u", port, 0xFFFF); } // Validate the specified server else if (!server || strlen(server) <= 0) { STHROWF("Attempting to connect IRC without specifying a server"); } // Validate the specified nickname else if (!nick || strlen(nick) <= 0) { STHROWF("Attempting to connect IRC without specifying a nickname"); } // Save the specified port m_Port = port; // Save the specified server m_Server.assign(server); // Save the specified nickname m_Nick.assign(nick); // Save the specified password m_Passwd.assign(passwd ? passwd : _SC("")); // Save the specified user m_User.assign(user ? user : _SC("")); // Save the specified name m_Name.assign(name ? name : _SC("")); // Enable the reconnection system m_Reconnect = true; // Reset the number of tries m_LeftTries = m_Tries; // Set the time-point for the next connection try m_NextTry = (SqMod_GetEpochTimeMicro() + (m_Wait * 1000LL)); // This is not an IPv6 connection m_IPv6 = false; // Attempt to connect the session and return the result return irc_connect(m_Session, m_Server.c_str(), m_Port, m_Passwd.empty() ? nullptr : m_Passwd.c_str(), m_Nick.c_str(), m_User.empty() ? nullptr : m_User.c_str(), m_Name.empty() ? nullptr : m_Name.c_str() ); } // ------------------------------------------------------------------------------------------------ Int32 Session::Connect6() { // Validate the handle Validate(); // Make sure we are allowed to connect IsNotConnected(); // Validate the specified server if (!m_Server.empty()) { STHROWF("Attempting to connect IRC without specifying a server"); } // Validate the specified nickname else if (!m_Nick.empty()) { STHROWF("Attempting to connect IRC without specifying a nickname"); } // Enable the reconnection system m_Reconnect = true; // Reset the number of tries m_LeftTries = m_Tries; // Set the time-point for the next try m_NextTry = (SqMod_GetEpochTimeMicro() + (m_Wait * 1000LL)); // This is an IPv6 connection m_IPv6 = true; // Attempt to connect the session and return the result return irc_connect6(m_Session, m_Server.c_str(), m_Port, m_Passwd.empty() ? nullptr : m_Passwd.c_str(), m_Nick.c_str(), m_User.empty() ? nullptr : m_User.c_str(), m_Name.empty() ? nullptr : m_Name.c_str() ); } // ------------------------------------------------------------------------------------------------ Int32 Session::Connect6(CSStr server, Uint32 port, CSStr nick, CSStr passwd, CSStr user, CSStr name) { // Validate the handle Validate(); // Make sure we are allowed to connect IsNotConnected(); // Validate the specified port if (port > 0xFFFF) { STHROWF("Port number is out of range: %u > %u", port, 0xFFFF); } // Validate the specified server else if (!server || strlen(server) <= 0) { STHROWF("Attempting to connect IRC without specifying a server"); } // Validate the specified nickname else if (!nick || strlen(nick) <= 0) { STHROWF("Attempting to connect IRC without specifying a nickname"); } // Save the specified port m_Port = port; // Save the specified server m_Server.assign(server); // Save the specified nickname m_Nick.assign(nick); // Save the specified password m_Passwd.assign(passwd ? passwd : _SC("")); // Save the specified user m_User.assign(user ? user : _SC("")); // Save the specified name m_Name.assign(name ? name : _SC("")); // Enable the reconnection system m_Reconnect = true; // Reset the number of tries m_LeftTries = m_Tries; // Set the time-point for the next connection try m_NextTry = (SqMod_GetEpochTimeMicro() + (m_Wait * 1000LL)); // This is an IPv6 connection m_IPv6 = true; // Attempt to connect the session and return the result return irc_connect6(m_Session, m_Server.c_str(), m_Port, m_Passwd.empty() ? nullptr : m_Passwd.c_str(), m_Nick.c_str(), m_User.empty() ? nullptr : m_User.c_str(), m_Name.empty() ? nullptr : m_Name.c_str() ); } // ------------------------------------------------------------------------------------------------ void Session::Disconnect() { if (IsConnected()) { // Update one last time to catch remaining events Update(); // Disable the reconnection system m_Reconnect = false; // Attempt to disconnect irc_disconnect(m_Session); } } // ------------------------------------------------------------------------------------------------ void Session::ForwardEvent(Function & listener, CCStr event, CCStr origin, CCStr * params, Uint32 count) { // Is there anyone even listening to this event? if (listener.IsNull()) { return; // No point in going forward } // Make sure that the origin can't be a null pointer else if (!origin) { origin = _SC(""); } // Each event must have an array of parameters (empty or not) Array parameters(DefaultVM::Get(), count); // Are the any parameters? if (params && count > 0) { // Transform the parameters into a squirrel array for (Uint32 i = 0; i < count; ++i) { parameters.SetValue(i, params[i]); } } // Attempt top execute the callback with the obtained values try { listener.Execute< CSStr, CSStr, Array & >(event, origin, parameters); } catch (const Sqrat::Exception & e) { SqMod_LogErr("IRC event [%s] => Squirrel error [%s]", event, e.what()); } catch (const std::exception & e) { SqMod_LogErr("IRC event [%s] => Program error [%s]", event, e.what()); } catch (...) { SqMod_LogErr("IRC event [%s] => Unknown error", event); } } // ------------------------------------------------------------------------------------------------ void Session::ForwardEvent(Function & listener, Uint32 event, CCStr origin, CCStr * params, Uint32 count) { // Is there anyone even listening to this event? if (listener.IsNull()) { return; // No point in going forward } // Make sure that the origin can't be a null pointer else if (!origin) { origin = _SC(""); } // Each event must have an array of parameters (empty or not) Array parameters(DefaultVM::Get(), count); // Are the any parameters? if (params && count > 0) { // Transform the parameters into a squirrel array for (unsigned int i = 0; i < count; ++i) { parameters.SetValue(i, params[i]); } } // Attempt top execute the callback with the obtained values try { listener.Execute< Uint32, CSStr, Array & >(event, origin, parameters); } catch (const Sqrat::Exception & e) { SqMod_LogErr("IRC event [%s] => Squirrel error [%s]", event, e.what()); } catch (const std::exception & e) { SqMod_LogErr("IRC event [%s] => Program error [%s]", event, e.what()); } catch (...) { SqMod_LogErr("IRC event [%s] => Unknown error", event); } } // ------------------------------------------------------------------------------------------------ void Session::ForwardEvent(Function & /*listener*/, CCStr /*nick*/, CCStr /*addr*/, irc_dcc_t /*dccid*/) { /* TODO! */ } // ------------------------------------------------------------------------------------------------ void Session::ForwardEvent(Function & /*listener*/, CCStr /*nick*/, CCStr /*addr*/, CCStr /*filename*/, Ulong /*size*/, irc_dcc_t /*dccid*/) { /* TODO! */ } // ------------------------------------------------------------------------------------------------ void Session::OnConnect(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { // Prevent any attempts to reconnect now inst->m_Reconnect = false; // Save the connection time-stamp to calculate session uptime inst->m_SessionTime = SqMod_GetEpochTimeMicro(); // Now forward event ForwardEvent(inst->m_OnConnect, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnNick(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { if (params && params[0]) { inst->m_Nick.assign(params[0]); } ForwardEvent(inst->m_OnNick, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnQuit(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnQuit, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnJoin(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnJoin, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnPart(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnPart, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnMode(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnMode, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnUmode(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnUmode, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnTopic(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnTopic, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnKick(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnKick, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnChannel(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnChannel, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnPrivMsg(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnPrivMsg, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnNotice(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnNotice, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnChannelNotice(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnChannelNotice, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnInvite(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnInvite, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnCtcpReq(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnCtcpReq, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnCtcpRep(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnCtcpRep, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnCtcpAction(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnCtcpAction, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnUnknown(irc_session_t * session, CCStr event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnUnknown, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnNumeric(irc_session_t * session, Uint32 event, CCStr origin, CCStr * params, Uint32 count) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnNumeric, event, origin, params, count); } } // ------------------------------------------------------------------------------------------------ void Session::OnDccChatReq(irc_session_t * session, CCStr nick, CCStr addr, irc_dcc_t dccid) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnDccChatReq, nick, addr, dccid); } } // ------------------------------------------------------------------------------------------------ void Session::OnDccSendReq(irc_session_t * session, CCStr nick, CCStr addr, CCStr filename, Ulong size, irc_dcc_t dccid) { Session * inst = reinterpret_cast< Session * >(irc_get_ctx(session)); if (ValidateEventSession(inst)) { ForwardEvent(inst->m_OnDccSendReq, nick, addr, filename, size, dccid); } } // ------------------------------------------------------------------------------------------------ static SQInteger FormattedIrcMessageCmd(HSQUIRRELVM vm, SendIrcMessageFunc send_func, bool colored) { // Obtain the initial stack size const Int32 top = sq_gettop(vm); // Do we have a target value? if (top <= 1) { return sq_throwerror(vm, "Missing the message target"); } // Do we have a message value? else if (top <= 2) { return sq_throwerror(vm, "Missing the message value"); } // The session instance Session * session = nullptr; // Attempt to extract the argument values try { session = Var< Session * >(vm, 1).value; } catch (const Sqrat::Exception & e) { // Propagate the error return sq_throwerror(vm, e.what()); } // Do we have a valid session instance? if (!session) { return sq_throwerror(vm, "Invalid session instance"); } // Do we have a valid session? else if (!session->IsValid()) { return sq_throwerror(vm, "Invalid IRC session"); } // Is the session connected? else if (!session->IsConnected()) { return sq_throwerror(vm, "Session is not connected"); } // Attempt to retrieve the target from the stack as a string StackStrF target(vm, 2, false); // Have we failed to retrieve the string? if (SQ_FAILED(target.mRes)) { return target.mRes; // Propagate the error! } // Attempt to retrieve the value from the stack as a string StackStrF message(vm, 3); // Have we failed to retrieve the string? if (SQ_FAILED(message.mRes)) { return message.mRes; // Propagate the error! } // Should we format this string for colored messages? if (colored) { // Attempt to scan the specified message for color formatting char * cmsg = irc_color_convert_to_mirc(message.mPtr, IrcAllocMem); // Validate the message if (!cmsg) { return sq_throwerror(vm, "Failed to convert the message colors"); } // Send the resulted message and push the returned result code onto the stack sq_pushinteger(vm, send_func(session->GetHandle(), target.mPtr, cmsg)); // Free the memory used to convert the message IrcFreeMem(cmsg); } // Forward the resulted string value and push the returned result code onto the stack else { sq_pushinteger(vm, send_func(session->GetHandle(), target.mPtr, message.mPtr)); } // We have a value on the stack return 1; } // ------------------------------------------------------------------------------------------------ SQInteger Session::CmdMsgF(HSQUIRRELVM vm) { return FormattedIrcMessageCmd(vm, irc_cmd_msg, false); } // ------------------------------------------------------------------------------------------------ SQInteger Session::CmdMeF(HSQUIRRELVM vm) { return FormattedIrcMessageCmd(vm, irc_cmd_me, false); } // ------------------------------------------------------------------------------------------------ SQInteger Session::CmdNoticeF(HSQUIRRELVM vm) { return FormattedIrcMessageCmd(vm, irc_cmd_notice, false); } // ------------------------------------------------------------------------------------------------ SQInteger Session::CmdColoredMsgF(HSQUIRRELVM vm) { return FormattedIrcMessageCmd(vm, irc_cmd_msg, true); } // ------------------------------------------------------------------------------------------------ SQInteger Session::CmdColoredMeF(HSQUIRRELVM vm) { return FormattedIrcMessageCmd(vm, irc_cmd_me, true); } // ------------------------------------------------------------------------------------------------ SQInteger Session::CmdColoredNoticeF(HSQUIRRELVM vm) { return FormattedIrcMessageCmd(vm, irc_cmd_notice, true); } /* ----------------------------------------------------------------------------------------------- * Helper function used to avoid having to include the session header. */ void SessionProcess() { Session::Process(); } /* ----------------------------------------------------------------------------------------------- * Helper function used to avoid having to include the session header. */ void SessionTerminate() { Session::Terminate(); } // ================================================================================================ void Register_Session(Table & ircns) { ircns.Bind(_SC("Session"), Class< Session, NoCopy< Session > >(ircns.GetVM(), _SC("SqIrcSession")) // Constructors .Ctor() // Core Meta-methods .Func(_SC("_cmp"), &Session::Cmp) .SquirrelFunc(_SC("_typename"), &Session::Typename) .Func(_SC("_tostring"), &Session::ToString) // Properties .Prop(_SC("Valid"), &Session::IsValid) .Prop(_SC("Connected"), &Session::IsConnected) .Prop(_SC("Tag"), &Session::GetTag, &Session::SetTag) .Prop(_SC("Data"), &Session::GetData, &Session::SetData) .Prop(_SC("Server"), &Session::GetServer, &Session::SetServer) .Prop(_SC("Password"), &Session::GetPassword, &Session::SetPassword) .Prop(_SC("Nick"), &Session::GetNick, &Session::SetNick) .Prop(_SC("User"), &Session::GetUser, &Session::SetUser) .Prop(_SC("Name"), &Session::GetName, &Session::SetName) .Prop(_SC("Port"), &Session::GetPort, &Session::SetPort) .Prop(_SC("PoolTime"), &Session::GetPoolTime, &Session::SetPoolTime) .Prop(_SC("LastCode"), &Session::GetLastCode) .Prop(_SC("Tries"), &Session::GetTries, &Session::SetTries) .Prop(_SC("Wait"), &Session::GetWait, &Session::SetWait) .Prop(_SC("LeftTries"), &Session::GetLeftTries, &Session::SetLeftTries) .Prop(_SC("NextTry"), &Session::GetNextTry, &Session::SetNextTry) .Prop(_SC("SessionTime"), &Session::GetSessionTime) .Prop(_SC("Reconnecting"), &Session::GetReconnect) .Prop(_SC("IPv6"), &Session::GetIPv6) .Prop(_SC("CtcpVersion"), (void (Session::*)(void))(nullptr), &Session::SetCtcpVersion) .Prop(_SC("ErrNo"), &Session::GetErrNo) .Prop(_SC("ErrStr"), &Session::GetErrStr) // Member Methods .Func(_SC("Bind"), &Session::BindEvent) .Func(_SC("GetEvent"), &Session::GetEvent) .Func(_SC("Disconnect"), &Session::Disconnect) .Func(_SC("CmdPart"), &Session::CmdPart) .Func(_SC("CmdInvite"), &Session::CmdInvite) .Func(_SC("CmdNames"), &Session::CmdNames) .Func(_SC("CmdMsg"), &Session::CmdMsg) .Func(_SC("CmdMe"), &Session::CmdMe) .Func(_SC("CmdNotice"), &Session::CmdNotice) .Func(_SC("CmdColoredMsg"), &Session::CmdColoredMsg) .Func(_SC("CmdColoredMe"), &Session::CmdColoredMe) .Func(_SC("CmdColoredNotice"), &Session::CmdColoredNotice) .Func(_SC("CmdCtcpRequest"), &Session::CmdCtcpRequest) .Func(_SC("CmdCtcpReply"), &Session::CmdCtcpReply) .Func(_SC("CmdNick"), &Session::CmdNick) .Func(_SC("CmdWhois"), &Session::CmdWhois) .Func(_SC("SendRaw"), &Session::SendRaw) .Func(_SC("DestroyDcc"), &Session::DestroyDcc) .Func(_SC("SetCtcpVersion"), &Session::SetCtcpVersion) .Func(_SC("SetOption"), &Session::SetOption) .Func(_SC("ResetOption"), &Session::ResetOption) // Member Overloads .Overload< Int32 (Session::*)(void) >(_SC("Connect"), &Session::Connect) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr) >(_SC("Connect"), &Session::Connect) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr, CSStr) >(_SC("Connect"), &Session::Connect) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr, CSStr, CSStr) >(_SC("Connect"), &Session::Connect) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr, CSStr, CSStr, CSStr) >(_SC("Connect"), &Session::Connect) .Overload< Int32 (Session::*)(void) >(_SC("Connect6"), &Session::Connect6) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr) >(_SC("Connect6"), &Session::Connect6) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr, CSStr) >(_SC("Connect6"), &Session::Connect6) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr, CSStr, CSStr) >(_SC("Connect6"), &Session::Connect6) .Overload< Int32 (Session::*)(CSStr, Uint32, CSStr, CSStr, CSStr, CSStr) >(_SC("Connect6"), &Session::Connect6) .Overload< Int32 (Session::*)(CSStr) >(_SC("CmdJoin"), &Session::CmdJoin) .Overload< Int32 (Session::*)(CSStr, CSStr) >(_SC("CmdJoin"), &Session::CmdJoin) .Overload< Int32 (Session::*)(void) >(_SC("CmdList"), &Session::CmdList) .Overload< Int32 (Session::*)(CSStr) >(_SC("CmdList"), &Session::CmdList) .Overload< Int32 (Session::*)(CSStr) >(_SC("CmdTopic"), &Session::CmdTopic) .Overload< Int32 (Session::*)(CSStr, CSStr) >(_SC("CmdTopic"), &Session::CmdTopic) .Overload< Int32 (Session::*)(CSStr) >(_SC("CmdChannelMode"), &Session::CmdChannelMode) .Overload< Int32 (Session::*)(CSStr, CSStr) >(_SC("CmdChannelMode"), &Session::CmdChannelMode) .Overload< Int32 (Session::*)(void) >(_SC("CmdUserMode"), &Session::CmdUserMode) .Overload< Int32 (Session::*)(CSStr) >(_SC("CmdUserMode"), &Session::CmdUserMode) .Overload< Int32 (Session::*)(CSStr, CSStr) >(_SC("CmdKick"), &Session::CmdKick) .Overload< Int32 (Session::*)(CSStr, CSStr, CSStr) >(_SC("CmdKick"), &Session::CmdKick) .Overload< Int32 (Session::*)(void) >(_SC("CmdQuit"), &Session::CmdQuit) .Overload< Int32 (Session::*)(CSStr) >(_SC("CmdQuit"), &Session::CmdQuit) // Squirrel Methods .SquirrelFunc(_SC("CmdMsgF"), &Session::CmdMsgF) .SquirrelFunc(_SC("CmdMeF"), &Session::CmdMeF) .SquirrelFunc(_SC("CmdNoticeF"), &Session::CmdNoticeF) .SquirrelFunc(_SC("CmdColoredMsgF"), &Session::CmdColoredMsgF) .SquirrelFunc(_SC("CmdColoredMeF"), &Session::CmdColoredMeF) .SquirrelFunc(_SC("CmdColoredNoticeF"), &Session::CmdColoredNoticeF) ); } } // Namespace:: SqMod