summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core.h12
-rw-r--r--ircserver.cpp109
-rw-r--r--window.cpp224
3 files changed, 338 insertions, 7 deletions
diff --git a/core.h b/core.h
index 8f7efdf..7ccc4ee 100644
--- a/core.h
+++ b/core.h
@@ -89,8 +89,15 @@ public:
virtual void handleUserInput(const char *str);
virtual void syncStateForClient(Buffer &output);
+ void handleNameReply(const char *str);
void handleJoin(const UserRef &user);
+ void handlePart(const UserRef &user, const char *message);
+ void handleQuit(const UserRef &user, const char *message);
+ void handleNick(const UserRef &user, const char *newNick);
+ void handleMode(const UserRef &user, const char *str);
void handlePrivmsg(const UserRef &user, const char *str);
+
+ char getEffectivePrefixChar(const char *nick) const;
};
@@ -273,6 +280,11 @@ public:
char serverPrefix[32], serverPrefixMode[32];
std::string serverChannelModes[4];
+ uint32_t getUserFlag(char search, const char *array) const;
+ uint32_t getUserFlagByPrefix(char prefix) const;
+ uint32_t getUserFlagByMode(char mode) const;
+ int getChannelModeType(char mode) const;
+
IRCServer(Bouncer *_bouncer);
~IRCServer();
diff --git a/ircserver.cpp b/ircserver.cpp
index 9fafd97..7e20310 100644
--- a/ircserver.cpp
+++ b/ircserver.cpp
@@ -186,31 +186,95 @@ void IRCServer::lineReceivedEvent(char *line, int size) {
if (strcmp(cmdBuf, "PING") == 0) {
char out[512];
- snprintf(out, 512, "PONG %s", allParams);
+ snprintf(out, 512, "PONG :%s", allParams);
sendLine(out);
+ return;
} else if (strcmp(cmdBuf, "JOIN") == 0) {
Channel *c = findChannel(targetBuf, true);
- if (c)
+ if (c) {
c->handleJoin(user);
+ return;
+ }
+
+ } else if (strcmp(cmdBuf, "PART") == 0) {
+ Channel *c = findChannel(targetBuf, false);
+ if (c) {
+ c->handlePart(user, paramsAfterFirst);;
+ return;
+ }
+
+ } else if (strcmp(cmdBuf, "QUIT") == 0) {
+ for (auto &i : channels)
+ i.second->handleQuit(user, allParams);
+ return;
+
+ } else if (strcmp(cmdBuf, "NICK") == 0) {
+ if (user.isSelf) {
+ strncpy(currentNick, allParams, sizeof(currentNick));
+ currentNick[sizeof(currentNick) - 1] = 0;
+
+ char buf[1024];
+ snprintf(buf, 1024, "You are now known as %s", currentNick);
+ status.pushMessage(buf);
+ }
+
+ for (auto &i : channels)
+ i.second->handleNick(user, allParams);
+ return;
+
+ } else if (strcmp(cmdBuf, "MODE") == 0) {
+ Channel *c = findChannel(targetBuf, false);
+ if (c) {
+ c->handleMode(user, paramsAfterFirst);
+ return;
+ }
} else if (strcmp(cmdBuf, "PRIVMSG") == 0) {
Channel *c = findChannel(targetBuf, true);
- if (c)
+ if (c) {
c->handlePrivmsg(user, paramsAfterFirst);
+ return;
+ }
} else if (strcmp(cmdBuf, "001") == 0) {
status.pushMessage("[debug: currentNick change detected]");
strncpy(currentNick, targetBuf, sizeof(currentNick));
currentNick[sizeof(currentNick) - 1] = 0;
+ return;
} else if (strcmp(cmdBuf, "005") == 0) {
processISupport(paramsAfterFirst);
+ return;
- } else {
- status.pushMessage("!! Unhandled !!");
+ } else if (strcmp(cmdBuf, "353") == 0) {
+ // RPL_NAMEREPLY:
+ // Target is always us
+ // Params: Channel privacy flag, channel, user list
+
+ char *space1 = strchr(paramsAfterFirst, ' ');
+ if (space1) {
+ char *space2 = strchr(space1 + 1, ' ');
+ if (space2) {
+ char *chanName = space1 + 1;
+ *space2 = 0;
+
+ char *userNames = space2 + 1;
+ if (*userNames == ':')
+ ++userNames;
+
+ Channel *c = findChannel(chanName, false);
+
+ if (c) {
+ c->handleNameReply(userNames);
+ return;
+ }
+ }
+ }
}
+
+ status.pushMessage("!! Unhandled !!");
}
@@ -289,3 +353,38 @@ void IRCServer::processISupport(const char *line) {
}
}
}
+
+
+uint32_t IRCServer::getUserFlag(char search, const char *array) const {
+ uint32_t flag = 1;
+
+ // Is this character a valid prefix?
+ while (*array != 0) {
+ if (*array == search)
+ return flag;
+
+ flag <<= 1;
+ ++array;
+ }
+}
+
+uint32_t IRCServer::getUserFlagByPrefix(char prefix) const {
+ return getUserFlag(prefix, serverPrefix);
+}
+uint32_t IRCServer::getUserFlagByMode(char mode) const {
+ return getUserFlag(mode, serverPrefixMode);
+}
+
+int IRCServer::getChannelModeType(char mode) const {
+ for (int i = 0; i < 4; i++) {
+ const char *modes = serverChannelModes[i].c_str();
+
+ while (*modes != 0) {
+ if (*modes == mode)
+ return i + 1;
+ ++modes;
+ }
+ }
+
+ return 0;
+}
diff --git a/window.cpp b/window.cpp
index 4f71982..f48164c 100644
--- a/window.cpp
+++ b/window.cpp
@@ -110,6 +110,43 @@ void Channel::syncStateForClient(Buffer &output) {
}
+void Channel::handleNameReply(const char *str) {
+ char copy[4096];
+ strncpy(copy, str, 4096);
+ copy[4095] = 0;
+
+ char *strtok_var;
+ char *name = strtok_r(copy, " ", &strtok_var);
+
+ while (name) {
+ uint32_t modes = 0;
+
+ // Check the beginning of the name for as many valid
+ // mode prefixes as possible
+ // Servers may only send one, but I want to take into
+ // account the possibility that they might send multiple
+ // ones. Just in case.
+
+ while (*name != 0) {
+ uint32_t flag = server->getUserFlagByPrefix(*name);
+
+ if (flag == 0)
+ break;
+ else
+ modes |= flag;
+
+ ++name;
+ }
+
+ // Got it!
+ users[name] = modes;
+
+ // TODO: push add command
+
+ // Get the next name
+ name = strtok_r(NULL, " ", &strtok_var);
+ }
+}
void Channel::handleJoin(const UserRef &user) {
if (user.isSelf) {
@@ -117,6 +154,9 @@ void Channel::handleJoin(const UserRef &user) {
inChannel = true;
pushMessage("You have joined the channel!");
} else {
+ users[user.nick] = 0;
+ // TODO: push add command
+
char buf[1024];
snprintf(buf, 1024,
"%s (%s@%s) has joined",
@@ -127,12 +167,192 @@ void Channel::handleJoin(const UserRef &user) {
pushMessage(buf);
}
}
+
+void Channel::handlePart(const UserRef &user, const char *message) {
+ auto i = users.find(user.nick);
+ if (i != users.end()) {
+ users.erase(i);
+ // TODO: push remove command
+ }
+
+ char buf[1024];
+
+ if (user.isSelf) {
+ inChannel = false;
+
+ snprintf(buf, 1024,
+ "You have left the channel (%s)",
+ message);
+ pushMessage(buf);
+ } else {
+ snprintf(buf, 1024,
+ "%s (%s@%s) has parted (%s)",
+ user.nick.c_str(),
+ user.ident.c_str(),
+ user.hostmask.c_str(),
+ message);
+
+ pushMessage(buf);
+ }
+}
+
+void Channel::handleQuit(const UserRef &user, const char *message) {
+ if (user.isSelf)
+ inChannel = false;
+
+ auto i = users.find(user.nick);
+ if (i == users.end())
+ return;
+
+ users.erase(i);
+ // TODO: push remove command
+ pushMessage("Removed from users");
+
+ char buf[1024];
+
+ snprintf(buf, 1024,
+ "%s (%s@%s) has quit (%s)",
+ user.nick.c_str(),
+ user.ident.c_str(),
+ user.hostmask.c_str(),
+ message);
+
+ pushMessage(buf);
+}
+
+void Channel::handleNick(const UserRef &user, const char *newNick) {
+ auto i = users.find(user.nick);
+ if (i == users.end())
+ return;
+
+ users[newNick] = i->second;
+ users.erase(i);
+ // TODO: push rename command
+
+ char buf[1024];
+ snprintf(buf, 1024,
+ "%s is now known as %s",
+ user.nick.c_str(),
+ newNick);
+
+ pushMessage(buf);
+}
+
+void Channel::handleMode(const UserRef &user, const char *str) {
+ char copy[4096];
+ strncpy(copy, str, 4096);
+ copy[4095] = 0;
+
+ char *strtok_var;
+ char *modes = strtok_r(copy, " ", &strtok_var);
+
+ if (!modes)
+ return;
+
+ bool addFlag = true;
+
+ while (*modes != 0) {
+ char mode = *(modes++);
+
+ uint32_t flag;
+
+ if (mode == '+') {
+ addFlag = true;
+ } else if (mode == '-') {
+ addFlag = false;
+
+ } else if ((flag = server->getUserFlagByMode(mode)) != 0) {
+ bool oops = false;
+ char *target = strtok_r(NULL, " ", &strtok_var);
+
+ auto i = users.find(target);
+ if (i == users.end()) {
+ // Oops? Spit out an error...
+ oops = true;
+ } else {
+ // TODO: push mode change to clients
+ uint32_t flags = i->second;
+ if (addFlag)
+ flags |= flag;
+ else
+ flags &= ~flag;
+ users[target] = flags;
+ }
+
+ char buf[1024];
+ snprintf(buf, 1024,
+ "%s %s mode %c on %s%s",
+ user.nick.c_str(),
+ addFlag ? "set" : "cleared",
+ mode,
+ target,
+ oops ? ", but something went wrong!" : "");
+ pushMessage(buf);
+
+ } else {
+ int type = server->getChannelModeType(mode);
+ char *param = 0;
+
+ switch (type) {
+ case 1:
+ case 2:
+ // Always get a parameter
+ param = strtok_r(NULL, " ", &strtok_var);
+ break;
+ case 3:
+ // Only get a parameter if adding
+ if (addFlag)
+ param = strtok_r(NULL, " ", &strtok_var);
+ break;
+ }
+
+ char buf[1024];
+ snprintf(buf, 1024,
+ "%s %s channel mode %c%s%s",
+ user.nick.c_str(),
+ addFlag ? "set" : "cleared",
+ mode,
+ param ? " " : "",
+ param ? param : "");
+ pushMessage(buf);
+ }
+ }
+}
+
void Channel::handlePrivmsg(const UserRef &user, const char *str) {
+ char prefix[2];
+ prefix[0] = getEffectivePrefixChar(user.nick.c_str());
+ prefix[1] = 0;
+
char buf[15000];
snprintf(buf, 15000,
- "<%s> %s",
+ "<%s%s> %s",
+ prefix,
user.nick.c_str(),
str);
-
+
pushMessage(buf);
}
+
+
+char Channel::getEffectivePrefixChar(const char *nick) const {
+ auto i = users.find(nick);
+ if (i == users.end())
+ return 0;
+
+ // Maybe this bit would work best as an IRCServer method?
+
+ uint32_t modes = i->second;
+ uint32_t flag = 1;
+ char *prefixes = server->serverPrefix;
+
+ while (*prefixes != 0) {
+ if (modes & flag)
+ return *prefixes;
+
+ ++prefixes;
+ flag <<= 1;
+ }
+
+ return 0;
+}