From 57f49f495d799a435b95d72021c004752628994b Mon Sep 17 00:00:00 2001 From: Treeki Date: Thu, 23 Jan 2014 23:09:38 +0100 Subject: sync user lists to clients, add support to python client, other assorted fixes --- core.h | 8 ++++ netcore.cpp | 6 +++ python_client.py | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- window.cpp | 116 ++++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 248 insertions(+), 8 deletions(-) diff --git a/core.h b/core.h index 2696032..7d2f052 100644 --- a/core.h +++ b/core.h @@ -155,9 +155,15 @@ struct Packet { B2C_WINDOW_ADD = 0x100, B2C_WINDOW_REMOVE = 0x101, B2C_WINDOW_MESSAGE = 0x102, + B2C_WINDOW_RENAME = 0x103, C2B_WINDOW_INPUT = 0x102, + B2C_CHANNEL_USER_ADD = 0x120, + B2C_CHANNEL_USER_REMOVE = 0x121, + B2C_CHANNEL_USER_RENAME = 0x122, + B2C_CHANNEL_USER_MODES = 0x123, + C2B_OOB_LOGIN = 0x8001, B2C_OOB_LOGIN_SUCCESS = 0x8001, @@ -317,6 +323,8 @@ public: int clientCount; int serverCount; + void sendToClients(Packet::Type type, const Buffer &data); + std::list windows; int nextWindowID; diff --git a/netcore.cpp b/netcore.cpp index 4341787..7082f9b 100644 --- a/netcore.cpp +++ b/netcore.cpp @@ -296,6 +296,12 @@ Window *NetCore::findWindow(int id) const { } +void NetCore::sendToClients(Packet::Type type, const Buffer &data) { + for (int i = 0; i < clientCount; i++) + if (clients[i]->isAuthed()) + clients[i]->sendPacket(type, data); +} + Client *Bouncer::constructClient() { diff --git a/python_client.py b/python_client.py index 88ac307..39411c5 100644 --- a/python_client.py +++ b/python_client.py @@ -137,6 +137,7 @@ class WindowTab(QtWidgets.QWidget): self.input = QtWidgets.QLineEdit(self) self.input.returnPressed.connect(self.handleLineEntered) + def makeLayout(self): layout = QtWidgets.QVBoxLayout(self) layout.addWidget(self.output) layout.addWidget(self.input) @@ -160,6 +161,105 @@ class WindowTab(QtWidgets.QWidget): if isAtEnd: self.output.setTextCursor(cursor) +class ChannelTab(WindowTab): + def __init__(self, parent=None): + WindowTab.__init__(self, parent) + + self.userList = QtWidgets.QListWidget(self) + + def makeLayout(self): + sublayout = QtWidgets.QVBoxLayout() + sublayout.addWidget(self.output) + sublayout.addWidget(self.input) + + layout = QtWidgets.QHBoxLayout(self) + layout.addLayout(sublayout) + layout.addWidget(self.userList) + + def readJunk(self, pdata, pos): + userCount = u32.unpack_from(pdata, pos)[0] + pos += 4 + + users = [] + for i in range(userCount): + #prefix = pdata[pos] + #pos += 1 + nicklen = u32.unpack_from(pdata, pos)[0] + pos += 4 + nick = pdata[pos:pos+nicklen].decode('utf-8', 'replace') + pos += nicklen + modes = u32.unpack_from(pdata, pos)[0] + pos += 4 + users.append(nick) + #self.userList.addItem(chr(prefix)+nick) + self.userList.addItem(nick) + + self.users = users + + topiclen = u32.unpack_from(pdata, pos)[0] + pos += 4 + self.topic = pdata[pos:pos+topiclen].decode('utf-8', 'replace') + pos += topiclen + + return pos + + def addUsers(self, pdata): + userCount = u32.unpack_from(pdata, 4)[0] + pos = 8 + + for i in range(userCount): + nicklen = u32.unpack_from(pdata, pos)[0] + pos += 4 + nick = pdata[pos:pos+nicklen].decode('utf-8', 'replace') + pos += nicklen + modes = u32.unpack_from(pdata, pos)[0] + pos += 4 + self.users.append(nick) + self.userList.addItem(nick) + + def removeUsers(self, pdata): + userCount = u32.unpack_from(pdata, 4)[0] + pos = 8 + if userCount == 0: + self.users = [] + self.userList.clear() + else: + for i in range(userCount): + nicklen = u32.unpack_from(pdata, pos)[0] + pos += 4 + nick = pdata[pos:pos+nicklen].decode('utf-8', 'replace') + pos += nicklen + print('Removing [%s]' % repr(nick)) + + self.users.remove(nick) + items = self.userList.findItems(nick, QtCore.Qt.MatchExactly) + self.userList.takeItem(self.userList.row(items[0])) + + def renameUser(self, pdata): + pos = 4 + nicklen = u32.unpack_from(pdata, pos)[0] + pos += 4 + fromnick = pdata[pos:pos+nicklen].decode('utf-8', 'replace') + pos += nicklen + nicklen = u32.unpack_from(pdata, pos)[0] + pos += 4 + tonick = pdata[pos:pos+nicklen].decode('utf-8', 'replace') + + try: + idx = self.users.index(fromnick) + except ValueError: + self.pushMessage('Crap, [%s] was not found in the users list!' % fromnick) + return + + self.users[idx] = tonick + + items = self.userList.findItems(fromnick, QtCore.Qt.MatchExactly) + items[0].setText(tonick) + + def changeUserMode(self, pdata): + # boop + pass + class MainWindow(QtWidgets.QMainWindow): def __init__(self, parent=None): @@ -177,6 +277,7 @@ class MainWindow(QtWidgets.QMainWindow): self.setCentralWidget(self.tabs) self.debugTab = WindowTab(self) + self.debugTab.makeLayout() self.debugTab.enteredMessage.connect(self.handleDebug) self.tabs.addTab(self.debugTab, 'Debug') @@ -211,7 +312,13 @@ class MainWindow(QtWidgets.QMainWindow): pos += msglen msgs.append(msg) - tab = WindowTab(self) + if wtype == 1: + tab = WindowTab(self) + elif wtype == 2: + tab = ChannelTab(self) + pos = tab.readJunk(pdata, pos) + + tab.makeLayout() tab.winID = wid tab.enteredMessage.connect(self.handleWindowInput) self.tabs.addTab(tab, wtitle) @@ -223,6 +330,23 @@ class MainWindow(QtWidgets.QMainWindow): msg = pdata[8:8+msglen].decode('utf-8', 'replace') self.tabLookup[wndID].pushMessage(msg) + elif ptype == 0x120: + # Add users to channel + wndID = u32.unpack_from(pdata, 0)[0] + self.tabLookup[wndID].addUsers(pdata) + elif ptype == 0x121: + # Remove users from channel + wndID = u32.unpack_from(pdata, 0)[0] + self.tabLookup[wndID].removeUsers(pdata) + elif ptype == 0x122: + # Rename user in channel + wndID = u32.unpack_from(pdata, 0)[0] + self.tabLookup[wndID].renameUser(pdata) + elif ptype == 0x123: + # Change user modes in channel + wndID = u32.unpack_from(pdata, 0)[0] + self.tabLookup[wndID].changeUserMode(pdata) + return True else: return QtWidgets.QMainWindow.event(self, event) diff --git a/window.cpp b/window.cpp index 00e3e22..c971046 100644 --- a/window.cpp +++ b/window.cpp @@ -99,14 +99,62 @@ int Channel::getType() const { } void Channel::handleUserInput(const char *str) { + char msgBuf[16384]; + if (str[0] == '/') { + if (strncmp(str, "/me ", 4) == 0) { + // The duplication of code between here and + // handlePrivmsg is ugly. TODO: fixme. + char prefix[2]; + prefix[0] = getEffectivePrefixChar(server->currentNick); + prefix[1] = 0; + + snprintf(msgBuf, sizeof(msgBuf), + "* %s%s %s", + prefix, + server->currentNick, + &str[4]); + pushMessage(msgBuf); + + snprintf(msgBuf, sizeof(msgBuf), + "PRIVMSG %s :\x01" "ACTION %s\x01", + name.c_str(), + &str[4]); + server->sendLine(msgBuf); + } } else { - server->sendLine(str); + // Aaaand this is also pretty ugly ><;; + // TODO: fixme. + char prefix[2]; + prefix[0] = getEffectivePrefixChar(server->currentNick); + prefix[1] = 0; + + snprintf(msgBuf, sizeof(msgBuf), + "<%s%s> %s", + prefix, + server->currentNick, + str); + pushMessage(msgBuf); + + snprintf(msgBuf, sizeof(msgBuf), + "PRIVMSG %s :%s", + name.c_str(), + str); + server->sendLine(msgBuf); } } void Channel::syncStateForClient(Buffer &output) { Window::syncStateForClient(output); + + output.writeU32(users.size()); + + for (auto &i : users) { + output.writeStr(i.first.c_str()); + output.writeU32(i.second); + } + + output.writeStr(topic.c_str()); } @@ -118,6 +166,12 @@ void Channel::handleNameReply(const char *str) { char *strtok_var; char *name = strtok_r(copy, " ", &strtok_var); + int nameCount = 0; + + Buffer packet; + packet.writeU32(id); + packet.writeU32(0); // Dummy value..! + while (name) { uint32_t modes = 0; @@ -141,21 +195,49 @@ void Channel::handleNameReply(const char *str) { // Got it! users[name] = modes; - // TODO: push add command + nameCount++; + //packet.writeU8(getEffectivePrefixChar(name)); + packet.writeStr(name); + packet.writeU32(modes); // Get the next name name = strtok_r(NULL, " ", &strtok_var); } + + if (nameCount > 0) { + uint32_t nameCountU32 = nameCount; + memcpy(&packet.data()[4], &nameCountU32, sizeof(uint32_t)); + + server->bouncer->sendToClients( + Packet::B2C_CHANNEL_USER_ADD, packet); + } } void Channel::handleJoin(const UserRef &user) { if (user.isSelf) { + Buffer packet; + packet.writeU32(id); + packet.writeU32(0); + + server->bouncer->sendToClients( + Packet::B2C_CHANNEL_USER_REMOVE, packet); + + users.clear(); + inChannel = true; pushMessage("You have joined the channel!"); } else { + Buffer packet; + packet.writeU32(id); + packet.writeU32(1); + packet.writeStr(user.nick.c_str()); + packet.writeU32(0); + + server->bouncer->sendToClients( + Packet::B2C_CHANNEL_USER_ADD, packet); + users[user.nick] = 0; - // TODO: push add command char buf[1024]; snprintf(buf, 1024, @@ -172,7 +254,14 @@ 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 + + Buffer packet; + packet.writeU32(id); + packet.writeU32(1); + packet.writeStr(user.nick.c_str()); + + server->bouncer->sendToClients( + Packet::B2C_CHANNEL_USER_REMOVE, packet); } char buf[1024]; @@ -205,8 +294,14 @@ void Channel::handleQuit(const UserRef &user, const char *message) { return; users.erase(i); - // TODO: push remove command - pushMessage("Removed from users"); + + Buffer packet; + packet.writeU32(id); + packet.writeU32(1); + packet.writeStr(user.nick.c_str()); + + server->bouncer->sendToClients( + Packet::B2C_CHANNEL_USER_REMOVE, packet); char buf[1024]; @@ -227,7 +322,14 @@ void Channel::handleNick(const UserRef &user, const char *newNick) { users[newNick] = i->second; users.erase(i); - // TODO: push rename command + + Buffer packet; + packet.writeU32(id); + packet.writeStr(user.nick.c_str()); + packet.writeStr(newNick); + + server->bouncer->sendToClients( + Packet::B2C_CHANNEL_USER_RENAME, packet); char buf[1024]; snprintf(buf, 1024, -- cgit v1.2.3