summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core.h40
-rw-r--r--ircserver.cpp244
-rw-r--r--window.cpp63
3 files changed, 345 insertions, 2 deletions
diff --git a/core.h b/core.h
index 3d9fba6..8f7efdf 100644
--- a/core.h
+++ b/core.h
@@ -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;
+ }
+ }
+ }
}
diff --git a/window.cpp b/window.cpp
index 5fc094a..4f71982 100644
--- a/window.cpp
+++ b/window.cpp
@@ -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);
+}