diff options
-rw-r--r-- | core.h | 12 | ||||
-rw-r--r-- | ircserver.cpp | 109 | ||||
-rw-r--r-- | window.cpp | 224 |
3 files changed, 338 insertions, 7 deletions
@@ -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; +} @@ -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; +} |