diff options
-rw-r--r-- | core.h | 40 | ||||
-rw-r--r-- | ircserver.cpp | 244 | ||||
-rw-r--r-- | window.cpp | 63 |
3 files changed, 345 insertions, 2 deletions
@@ -15,6 +15,7 @@ #include <netinet/in.h> #include <gnutls/gnutls.h> #include <list> +#include <map> #include <string> #include "buffer.h" @@ -34,6 +35,14 @@ class NetCore; class Bouncer; class IRCServer; +// Need to move this somewhere more appropriate +struct UserRef { + std::string nick, ident, hostmask; + bool isSelf, isValid; + + UserRef() { isSelf = false; isValid = false; } +}; + class Window { public: @@ -64,6 +73,26 @@ public: virtual void handleUserInput(const char *str); }; +class Channel : public Window { +public: + Channel(IRCServer *_server, const char *_name); + + IRCServer *server; + + bool inChannel; + + std::string name, topic; + std::map<std::string, uint32_t> users; + + virtual const char *getTitle() const; + virtual int getType() const; + virtual void handleUserInput(const char *str); + virtual void syncStateForClient(Buffer &output); + + void handleJoin(const UserRef &user); + void handlePrivmsg(const UserRef &user, const char *str); +}; + class SocketRWCommon { @@ -236,9 +265,14 @@ public: Bouncer *bouncer; StatusWindow status; + std::map<std::string, Channel *> channels; IRCNetworkConfig config; + char currentNick[128]; + char serverPrefix[32], serverPrefixMode[32]; + std::string serverChannelModes[4]; + IRCServer(Bouncer *_bouncer); ~IRCServer(); @@ -251,6 +285,12 @@ private: virtual void lineReceivedEvent(char *line, int size); virtual void attachedToCore(); + + + void resetIRCState(); + void processISupport(const char *str); + + Channel *findChannel(const char *name, bool createIfNeeded); }; diff --git a/ircserver.cpp b/ircserver.cpp index f2189b2..9fafd97 100644 --- a/ircserver.cpp +++ b/ircserver.cpp @@ -9,6 +9,11 @@ IRCServer::IRCServer(Bouncer *_bouncer) : IRCServer::~IRCServer() { bouncer->deregisterWindow(&status); + + for (auto &i : channels) { + bouncer->deregisterWindow(i.second); + delete i.second; + } } void IRCServer::attachedToCore() { @@ -21,7 +26,36 @@ void IRCServer::connect() { } +void IRCServer::resetIRCState() { + strcpy(currentNick, ""); + + strcpy(serverPrefix, "@+"); + strcpy(serverPrefixMode, "ov"); +} + + +Channel *IRCServer::findChannel(const char *name, bool createIfNeeded) { + std::map<std::string, Channel *>::iterator + check = channels.find(name); + + if (check == channels.end()) { + if (createIfNeeded) { + Channel *c = new Channel(this, name); + channels[name] = c; + return c; + } else { + return 0; + } + } else { + return check->second; + } +} + + + void IRCServer::connectedEvent() { + resetIRCState(); + printf("[IRCServer:%p] connectedEvent\n", this); status.pushMessage("Connected, identifying to IRC..."); @@ -44,4 +78,214 @@ void IRCServer::lineReceivedEvent(char *line, int size) { printf("[%d] { %s }\n", size, line); status.pushMessage(line); + + + // Process this line...! + UserRef user; + + // Is there a prefix? + if (line[0] == ':') { + char nickBuf[512], identBuf[512], hostBuf[512]; + int nickPos = 0, identPos = 0, hostPos = 0; + int phase = 0; + + ++line; // skip colon + + while ((*line != ' ') && (*line != 0)) { + if (phase == 0) { + // Nick + if (*line == '!') + phase = 1; + else if (*line == '@') + phase = 2; + else { + if (nickPos < 511) + nickBuf[nickPos++] = *line; + } + } else if (phase == 1) { + // Ident + if (*line == '@') + phase = 2; + else { + if (identPos < 511) + identBuf[identPos++] = *line; + } + } else if (phase == 2) { + if (hostPos < 511) + hostBuf[hostPos++] = *line; + } + + ++line; + } + + if (*line == 0) { + // Invalid line. Can't parse this. + return; + } + + ++line; // skip the space + + nickBuf[nickPos] = 0; + identBuf[identPos] = 0; + hostBuf[hostPos] = 0; + + user.nick = nickBuf; + user.ident = identBuf; + user.hostmask = hostBuf; + + user.isValid = true; + user.isSelf = (strcmp(nickBuf, currentNick) == 0); + } + + // Get the command + char cmdBuf[512]; + int cmdPos = 0; + + while ((*line != ' ') && (*line != 0)) { + if (cmdPos < 511) + cmdBuf[cmdPos++] = *line; + ++line; + } + cmdBuf[cmdPos] = 0; + + if (*line == 0) { + // Invalid line. + return; + } + + ++line; // skip the space + + // Skip the : if there is one + if (*line == ':') + ++line; + + // Get the first param, or "target" in many cases + char *allParams = line; + char *paramsAfterFirst = line; + + char targetBuf[512]; + int targetPos = 0; + + while ((*paramsAfterFirst != ' ') && (*paramsAfterFirst != 0)) { + if (targetPos < 511) + targetBuf[targetPos++] = *paramsAfterFirst; + ++paramsAfterFirst; + } + + targetBuf[targetPos] = 0; + + // If we didn't reach the end of the line, skip the space + if (*paramsAfterFirst == ' ') + ++paramsAfterFirst; + + // And if the params begin with :, skip it + if (*paramsAfterFirst == ':') + ++paramsAfterFirst; + + // Now figure out what to do with this...! + + if (strcmp(cmdBuf, "PING") == 0) { + char out[512]; + snprintf(out, 512, "PONG %s", allParams); + sendLine(out); + + } else if (strcmp(cmdBuf, "JOIN") == 0) { + Channel *c = findChannel(targetBuf, true); + if (c) + c->handleJoin(user); + + } else if (strcmp(cmdBuf, "PRIVMSG") == 0) { + Channel *c = findChannel(targetBuf, true); + if (c) + c->handlePrivmsg(user, paramsAfterFirst); + + } else if (strcmp(cmdBuf, "001") == 0) { + status.pushMessage("[debug: currentNick change detected]"); + + strncpy(currentNick, targetBuf, sizeof(currentNick)); + currentNick[sizeof(currentNick) - 1] = 0; + + } else if (strcmp(cmdBuf, "005") == 0) { + processISupport(paramsAfterFirst); + + } else { + status.pushMessage("!! Unhandled !!"); + } +} + + +void IRCServer::processISupport(const char *line) { + while (*line != 0) { + char keyBuf[512], valueBuf[512]; + int keyPos = 0, valuePos = 0; + int phase = 0; + + // This means we've reached the end + if (*line == ':') + return; + + while ((*line != 0) && (*line != ' ')) { + if (phase == 0) { + if (*line == '=') + phase = 1; + else if (keyPos < 511) + keyBuf[keyPos++] = *line; + } else { + if (valuePos < 511) + valueBuf[valuePos++] = *line; + } + + ++line; + } + + if (*line == ' ') + ++line; + + keyBuf[keyPos] = 0; + valueBuf[valuePos] = 0; + + + // Now process the thing + + if (strcmp(keyBuf, "PREFIX") == 0) { + int prefixCount = (valuePos - 2) / 2; + + if (valueBuf[0] == '(' && valueBuf[1+prefixCount] == ')') { + if (prefixCount < 32) { + strncpy(serverPrefixMode, &valueBuf[1], prefixCount); + strncpy(serverPrefix, &valueBuf[2+prefixCount], prefixCount); + + serverPrefixMode[prefixCount] = 0; + serverPrefix[prefixCount] = 0; + } + } + } else if (strcmp(keyBuf, "CHANMODES") == 0) { + char *proc = &valueBuf[0]; + + for (int index = 0; index < 4; index++) { + if (*proc == 0) + break; + + char *start = proc; + char *end = proc; + + while ((*end != ',') && (*end != 0)) + ++end; + + // If this is a zero, we can't read any more + bool endsHere = (*end == 0); + *end = 0; + + serverChannelModes[index] = start; + char moof[1000]; + sprintf(moof, "set chanmodes %d to [%s]", index, serverChannelModes[index].c_str()); + status.pushMessage(moof); + + if (endsHere) + break; + else + proc = end + 1; + } + } + } } @@ -43,9 +43,9 @@ void Window::pushMessage(const char *str) { StatusWindow::StatusWindow(IRCServer *_server) : - Window(_server->bouncer) + Window(_server->bouncer), + server(_server) { - server = _server; } const char *StatusWindow::getTitle() const { @@ -77,3 +77,62 @@ void StatusWindow::handleUserInput(const char *str) { server->sendLine(str); } } + + + + +Channel::Channel(IRCServer *_server, const char *_name) : + Window(_server->bouncer), + server(_server), + inChannel(false), + name(_name) +{ + server->bouncer->registerWindow(this); +} + +const char *Channel::getTitle() const { + return name.c_str(); +} + +int Channel::getType() const { + return 2; +} + +void Channel::handleUserInput(const char *str) { + if (str[0] == '/') { + } else { + server->sendLine(str); + } +} + +void Channel::syncStateForClient(Buffer &output) { + Window::syncStateForClient(output); +} + + + +void Channel::handleJoin(const UserRef &user) { + if (user.isSelf) { + users.clear(); + inChannel = true; + pushMessage("You have joined the channel!"); + } else { + char buf[1024]; + snprintf(buf, 1024, + "%s (%s@%s) has joined", + user.nick.c_str(), + user.ident.c_str(), + user.hostmask.c_str()); + + pushMessage(buf); + } +} +void Channel::handlePrivmsg(const UserRef &user, const char *str) { + char buf[15000]; + snprintf(buf, 15000, + "<%s> %s", + user.nick.c_str(), + str); + + pushMessage(buf); +} |