summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sh2
-rw-r--r--core.h60
-rw-r--r--ircserver.cpp24
-rw-r--r--mobileclient.cpp38
-rw-r--r--netcore.cpp49
-rw-r--r--python_client.py206
-rw-r--r--socketcommon.cpp2
-rw-r--r--window.cpp79
8 files changed, 397 insertions, 63 deletions
diff --git a/build.sh b/build.sh
index 2bfdfb3..72a77af 100755
--- a/build.sh
+++ b/build.sh
@@ -2,7 +2,7 @@
mkdir -p binary
NETCODE="socketcommon.cpp client.cpp mobileclient.cpp server.cpp ircserver.cpp netcore.cpp"
-SOURCES="$NETCODE main.cpp dns.cpp"
+SOURCES="$NETCODE main.cpp window.cpp dns.cpp"
FLAGS="-std=c++11 -lgnutls -pthread -g"
g++ -o binary/nb4 $FLAGS $SOURCES
diff --git a/core.h b/core.h
index 4037f25..3d9fba6 100644
--- a/core.h
+++ b/core.h
@@ -15,6 +15,7 @@
#include <netinet/in.h>
#include <gnutls/gnutls.h>
#include <list>
+#include <string>
#include "buffer.h"
@@ -31,6 +32,39 @@
class NetCore;
class Bouncer;
+class IRCServer;
+
+
+class Window {
+public:
+ NetCore *core;
+
+ Window(NetCore *_core);
+ virtual ~Window() { }
+
+ int id;
+ std::list<std::string> messages;
+
+ virtual const char *getTitle() const = 0;
+ virtual int getType() const = 0;
+ virtual void syncStateForClient(Buffer &output);
+ virtual void handleUserInput(const char *str) { }
+
+ void pushMessage(const char *str);
+};
+
+class StatusWindow : public Window {
+public:
+ StatusWindow(IRCServer *_server);
+
+ IRCServer *server;
+
+ virtual const char *getTitle() const;
+ virtual int getType() const;
+ virtual void handleUserInput(const char *str);
+};
+
+
class SocketRWCommon {
public:
@@ -80,6 +114,12 @@ struct Packet {
C2B_COMMAND = 1,
B2C_STATUS = 1,
+ B2C_WINDOW_ADD = 0x100,
+ B2C_WINDOW_REMOVE = 0x101,
+ B2C_WINDOW_MESSAGE = 0x102,
+
+ C2B_WINDOW_INPUT = 0x102,
+
C2B_OOB_LOGIN = 0x8001,
B2C_OOB_LOGIN_SUCCESS = 0x8001,
@@ -160,7 +200,7 @@ class Server : private SocketRWCommon {
public:
Server(NetCore *_netCore);
- ~Server();
+ virtual ~Server();
protected:
void connect(const char *hostname, int _port, bool _useTls);
@@ -174,10 +214,11 @@ private:
void connectionSuccessful();
void processReadBuffer();
-private:
virtual void connectedEvent() = 0;
virtual void disconnectedEvent() = 0;
virtual void lineReceivedEvent(char *line, int size) = 0;
+
+ virtual void attachedToCore() { }
};
struct IRCNetworkConfig {
@@ -191,11 +232,15 @@ struct IRCNetworkConfig {
};
class IRCServer : public Server {
- Bouncer *bouncer;
public:
+ Bouncer *bouncer;
+
+ StatusWindow status;
+
IRCNetworkConfig config;
IRCServer(Bouncer *_bouncer);
+ ~IRCServer();
void connect();
@@ -204,6 +249,8 @@ private:
virtual void connectedEvent();
virtual void disconnectedEvent();
virtual void lineReceivedEvent(char *line, int size);
+
+ virtual void attachedToCore();
};
@@ -216,6 +263,13 @@ public:
int clientCount;
int serverCount;
+ std::list<Window *> windows;
+ int nextWindowID;
+
+ int registerWindow(Window *window);
+ void deregisterWindow(Window *window);
+ Window *findWindow(int id) const;
+
bool quitFlag;
int execute();
diff --git a/ircserver.cpp b/ircserver.cpp
index 830c82b..f2189b2 100644
--- a/ircserver.cpp
+++ b/ircserver.cpp
@@ -1,16 +1,29 @@
#include "core.h"
-IRCServer::IRCServer(Bouncer *_bouncer) : Server(_bouncer) {
- bouncer = _bouncer;
+IRCServer::IRCServer(Bouncer *_bouncer) :
+ Server(_bouncer),
+ bouncer(_bouncer),
+ status(this)
+{
+}
+
+IRCServer::~IRCServer() {
+ bouncer->deregisterWindow(&status);
+}
+
+void IRCServer::attachedToCore() {
+ bouncer->registerWindow(&status);
}
void IRCServer::connect() {
+ status.pushMessage("Connecting...");
Server::connect(config.hostname, config.port, config.useTls);
}
void IRCServer::connectedEvent() {
printf("[IRCServer:%p] connectedEvent\n", this);
+ status.pushMessage("Connected, identifying to IRC...");
char buf[2048];
@@ -25,13 +38,10 @@ void IRCServer::connectedEvent() {
}
void IRCServer::disconnectedEvent() {
printf("[IRCServer:%p] disconnectedEvent\n", this);
+ status.pushMessage("Disconnected.");
}
void IRCServer::lineReceivedEvent(char *line, int size) {
printf("[%d] { %s }\n", size, line);
- Buffer pkt;
- pkt.writeStr(line, size);
- for (int i = 0; i < bouncer->clientCount; i++)
- if (bouncer->clients[i]->isAuthed())
- bouncer->clients[i]->sendPacket(Packet::B2C_STATUS, pkt);
+ status.pushMessage(line);
}
diff --git a/mobileclient.cpp b/mobileclient.cpp
index bbfac73..fb8b3c1 100644
--- a/mobileclient.cpp
+++ b/mobileclient.cpp
@@ -6,6 +6,18 @@ MobileClient::MobileClient(Bouncer *_bouncer) : Client(_bouncer) {
void MobileClient::sessionStartEvent() {
printf("{Session started}\n");
+
+ Buffer syncPacket;
+ syncPacket.writeU32(bouncer->windows.size());
+
+ std::list<Window *>::iterator
+ i = bouncer->windows.begin(),
+ e = bouncer->windows.end();
+
+ for (; i != e; ++i)
+ (*i)->syncStateForClient(syncPacket);
+
+ sendPacket(Packet::B2C_WINDOW_ADD, syncPacket);
}
void MobileClient::sessionEndEvent() {
printf("{Session ended}\n");
@@ -16,6 +28,19 @@ void MobileClient::packetReceivedEvent(Packet::Type type, Buffer &pkt) {
pkt.readStr(cmd, sizeof(cmd));
handleDebugCommand(cmd, strlen(cmd));
+ } else if (type == Packet::C2B_WINDOW_INPUT) {
+ int winID = pkt.readU32();
+ Window *window = bouncer->findWindow(winID);
+ if (!window) {
+ printf("[MobileClient:%p] Message for unknown window %d\n", this, winID);
+ return;
+ }
+
+ char text[8192];
+ pkt.readStr(text, sizeof(text));
+
+ window->handleUserInput(text);
+
} else {
printf("[MobileClient:%p] Unrecognised packet for MobileClient: type %d, size %d\n",
this, type, pkt.size());
@@ -47,19 +72,6 @@ void MobileClient::handleDebugCommand(char *line, int size) {
pkt.writeStr("Your wish is my command!");
for (int i = 0; i < bouncer->clientCount; i++)
bouncer->clients[i]->sendPacket(Packet::B2C_STATUS, pkt);
-
- } else if (strncmp(line, "srvpw", 5) == 0) {
- int sid = line[5] - '0';
- // ugly hack, fuck casting, will fix later
- strcpy(((IRCServer*)bouncer->servers[sid])->config.password, &line[7]);
-
- } else if (strncmp(line, "connsrv", 7) == 0) {
- int sid = line[7] - '0';
- // ugly hack, fuck casting, will fix later
- ((IRCServer*)bouncer->servers[sid])->connect();
- } else if (line[0] >= '0' && line[0] <= '9') {
- int sid = line[0] - '0';
- bouncer->servers[sid]->sendLine(&line[1]);
}
}
diff --git a/netcore.cpp b/netcore.cpp
index 27769a6..4341787 100644
--- a/netcore.cpp
+++ b/netcore.cpp
@@ -8,6 +8,8 @@ NetCore::NetCore() {
serverCount = 0;
for (int i = 0; i < SERVER_LIMIT; i++)
servers[i] = NULL;
+
+ nextWindowID = 1;
}
Client *NetCore::findClientWithSessionKey(uint8_t *key) const {
@@ -24,6 +26,7 @@ int NetCore::registerServer(Server *server) {
int id = serverCount++;
servers[id] = server;
+ server->attachedToCore();
return id;
}
void NetCore::deregisterServer(int id) {
@@ -251,8 +254,54 @@ int NetCore::execute() {
}
+
+int NetCore::registerWindow(Window *window) {
+ window->id = nextWindowID;
+ nextWindowID++;
+
+ windows.push_back(window);
+
+
+ Buffer pkt;
+ pkt.writeU32(1);
+ window->syncStateForClient(pkt);
+
+ for (int i = 0; i < clientCount; i++)
+ if (clients[i]->isAuthed())
+ clients[i]->sendPacket(Packet::B2C_WINDOW_ADD, pkt);
+}
+
+void NetCore::deregisterWindow(Window *window) {
+ Buffer pkt;
+ pkt.writeU32(1);
+ pkt.writeU32(window->id);
+
+ for (int i = 0; i < clientCount; i++)
+ if (clients[i]->isAuthed())
+ clients[i]->sendPacket(Packet::B2C_WINDOW_REMOVE, pkt);
+
+ windows.remove(window);
+}
+
+Window *NetCore::findWindow(int id) const {
+ std::list<Window *>::const_iterator
+ i = windows.begin(),
+ e = windows.end();
+
+ for (; i != e; ++i)
+ if ((*i)->id == id)
+ return *i;
+
+ return 0;
+}
+
+
+
+
Client *Bouncer::constructClient() {
return new MobileClient(this);
}
+
+
diff --git a/python_client.py b/python_client.py
index 24c8a3e..88ac307 100644
--- a/python_client.py
+++ b/python_client.py
@@ -1,4 +1,9 @@
-import socket, ssl, threading, struct
+# Yes, this source code is terrible.
+# It's just something I've put together to test the server
+# before I write a *real* client.
+
+import sys, socket, ssl, threading, struct
+from PyQt5 import QtCore, QtGui, QtWidgets
protocolVer = 1
sock = None
@@ -9,6 +14,8 @@ lastReceivedPacketID = 0
packetCache = []
packetLock = threading.Lock()
+u32 = struct.Struct('<I')
+
class Packet:
def __init__(self, type, data):
global nextID
@@ -19,7 +26,7 @@ class Packet:
self.id = nextID
nextID = nextID + 1
- def sendOverWire(self):
+ def sendOverWire(self, sock):
header = struct.pack('<HHI', self.type, 0, len(self.data))
if (self.type & 0x8000) == 0:
extHeader = struct.pack('<II', self.id, lastReceivedPacketID)
@@ -41,9 +48,11 @@ def reader():
global lastReceivedPacketID, authed, sessionKey
readbuf = b''
+ sockCopy = sock
+
print('(Connected)')
while True:
- data = sock.recv(1024)
+ data = sockCopy.recv(1024)
if not data:
print('(Disconnected)')
break
@@ -78,18 +87,23 @@ def reader():
if type == 0x8001:
sessionKey = packetdata
authed = True
+ elif type == 0x8002:
+ print('FAILED!')
elif type == 0x8003:
authed = True
- pid = struct.unpack('<I', packetdata)[0]
+ pid = u32.unpack(packetdata)[0]
clearCachedPackets(pid)
try:
for packet in packetCache:
- packet.sendOverWire()
+ packet.sendOverWire(sockCopy)
except:
pass
- elif type == 1:
- strlen = struct.unpack_from('<I', packetdata, 0)[0]
- print(packetdata[4:4+strlen].decode('utf-8'))
+ else:
+ # Horrible kludge. I'm sorry.
+ # I didn't feel like rewriting this to use
+ # QObject and QThread. :(
+ packetEvent = PacketEvent(type, packetdata)
+ app.postEvent(mainwin, packetEvent)
pos += size
@@ -102,40 +116,156 @@ def writePacket(type, data, allowUnauthed=False):
packetCache.append(packet)
try:
if authed or allowUnauthed:
- packet.sendOverWire()
+ packet.sendOverWire(sock)
except:
pass
+class PacketEvent(QtCore.QEvent):
+ def __init__(self, ptype, pdata):
+ QtCore.QEvent.__init__(self, QtCore.QEvent.User)
+ self.packetType = ptype
+ self.packetData = pdata
+
+
+class WindowTab(QtWidgets.QWidget):
+ def __init__(self, parent=None):
+ QtWidgets.QWidget.__init__(self, parent)
+
+ self.output = QtWidgets.QTextEdit(self)
+ self.output.setReadOnly(True)
+ self.input = QtWidgets.QLineEdit(self)
+ self.input.returnPressed.connect(self.handleLineEntered)
+
+ layout = QtWidgets.QVBoxLayout(self)
+ layout.addWidget(self.output)
+ layout.addWidget(self.input)
+
+ enteredMessage = QtCore.pyqtSignal(str)
+ def handleLineEntered(self):
+ line = self.input.text()
+ self.input.setText('')
+
+ self.enteredMessage.emit(line)
+
+ def pushMessage(self, msg):
+ cursor = self.output.textCursor()
+
+ isAtEnd = cursor.atEnd()
+ cursor.movePosition(QtGui.QTextCursor.End)
+ cursor.clearSelection()
+ cursor.insertText(msg)
+ cursor.insertText('\n')
+
+ if isAtEnd:
+ self.output.setTextCursor(cursor)
+
+
+class MainWindow(QtWidgets.QMainWindow):
+ def __init__(self, parent=None):
+ QtWidgets.QMainWindow.__init__(self, parent)
+
+ self.setWindowTitle('Ninjifox\'s IRC Client Test')
+
+ tb = self.addToolBar('Main')
+ tb.addAction('Connect', self.handleConnect)
+ tb.addAction('Disconnect', self.handleDisconnect)
+ tb.addAction('Login', self.handleLogin)
+
+ self.tabs = QtWidgets.QTabWidget(self)
+ self.tabLookup = {}
+ self.setCentralWidget(self.tabs)
-while True:
- bit = input()
- bits = bit.split(' ', 1)
- cmd = bits[0]
-
- with packetLock:
- print('{')
- if cmd == 'connect':
- try:
- basesock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
- basesock.connect(('localhost', 5454))
- #sock = ssl.wrap_socket(basesock)
- sock = basesock
- thd = threading.Thread(None, reader)
- thd.start()
- except Exception as e:
- print(e)
- elif cmd == 'disconnect':
- sock.shutdown(socket.SHUT_RDWR)
- sock.close()
- sock = None
- authed = False
- elif cmd == 'login':
- writePacket(0x8001, struct.pack('<II 16s', protocolVer, lastReceivedPacketID, sessionKey), True)
- elif cmd == 'cmd':
- data = bits[1].encode('utf-8')
+ self.debugTab = WindowTab(self)
+ self.debugTab.enteredMessage.connect(self.handleDebug)
+ self.tabs.addTab(self.debugTab, 'Debug')
+
+ def event(self, event):
+ if event.type() == QtCore.QEvent.User:
+ event.accept()
+
+ ptype = event.packetType
+ pdata = event.packetData
+
+ if ptype == 1:
+ strlen = u32.unpack_from(pdata, 0)[0]
+ msg = pdata[4:4+strlen].decode('utf-8', 'replace')
+ self.debugTab.pushMessage(msg)
+ elif ptype == 0x100:
+ # ADD WINDOWS
+ wndCount = u32.unpack_from(pdata, 0)[0]
+ pos = 4
+
+ for i in range(wndCount):
+ wtype, wid, wtlen = struct.unpack_from('<III', pdata, pos)
+ pos += 12
+ wtitle = pdata[pos:pos+wtlen].decode('utf-8', 'replace')
+ pos += wtlen
+ msgCount = u32.unpack_from(pdata, pos)[0]
+ pos += 4
+ msgs = []
+ for j in range(msgCount):
+ msglen = u32.unpack_from(pdata, pos)[0]
+ pos += 4
+ msg = pdata[pos:pos+msglen].decode('utf-8', 'replace')
+ pos += msglen
+ msgs.append(msg)
+
+ tab = WindowTab(self)
+ tab.winID = wid
+ tab.enteredMessage.connect(self.handleWindowInput)
+ self.tabs.addTab(tab, wtitle)
+ self.tabLookup[wid] = tab
+ tab.pushMessage('\n'.join(msgs))
+ elif ptype == 0x102:
+ # WINDOW MESSAGES
+ wndID, msglen = struct.unpack_from('<II', pdata, 0)
+ msg = pdata[8:8+msglen].decode('utf-8', 'replace')
+ self.tabLookup[wndID].pushMessage(msg)
+
+ return True
+ else:
+ return QtWidgets.QMainWindow.event(self, event)
+
+ def handleConnect(self):
+ global sock
+ try:
+ basesock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
+ basesock.connect(('localhost', 5454))
+ #sock = ssl.wrap_socket(basesock)
+ sock = basesock
+ thd = threading.Thread(None, reader)
+ thd.daemon = True
+ thd.start()
+ except Exception as e:
+ print(e)
+
+ def handleDisconnect(self):
+ global sock, authed
+ sock.shutdown(socket.SHUT_RDWR)
+ sock.close()
+ sock = None
+ authed = False
+
+ def handleLogin(self):
+ writePacket(0x8001, struct.pack('<II 16s', protocolVer, lastReceivedPacketID, sessionKey), True)
+
+ def handleDebug(self, text):
+ with packetLock:
+ data = str(text).encode('utf-8')
writePacket(1, struct.pack('<I', len(data)) + data)
- elif cmd == 'quit':
- break
- print('}')
+
+ def handleWindowInput(self, text):
+ wid = self.sender().winID
+ with packetLock:
+ data = str(text).encode('utf-8')
+ writePacket(0x102, struct.pack('<II', wid, len(data)) + data)
+
+
+app = QtWidgets.QApplication(sys.argv)
+
+mainwin = MainWindow()
+mainwin.show()
+
+app.exec_()
diff --git a/socketcommon.cpp b/socketcommon.cpp
index e98b837..897bc58 100644
--- a/socketcommon.cpp
+++ b/socketcommon.cpp
@@ -135,7 +135,7 @@ void SocketRWCommon::writeAction() {
}
if (amount > 0) {
- printf("[fd=%d] Wrote %d bytes\n", sock, amount);
+ printf("[fd=%d] Wrote %d bytes out of %d\n", sock, amount, outputBuf.size());
outputBuf.trimFromStart(amount);
} else if (amount == 0)
printf("Sent 0!\n");
diff --git a/window.cpp b/window.cpp
new file mode 100644
index 0000000..5fc094a
--- /dev/null
+++ b/window.cpp
@@ -0,0 +1,79 @@
+#include "core.h"
+
+Window::Window(NetCore *_core) {
+ core = _core;
+}
+
+void Window::syncStateForClient(Buffer &output) {
+ output.writeU32(getType());
+ output.writeU32(id);
+ output.writeStr(getTitle());
+
+ output.writeU32(messages.size());
+
+ std::list<std::string>::iterator
+ i = messages.begin(),
+ e = messages.end();
+
+ for (; i != e; ++i) {
+ output.writeStr(i->c_str());
+ }
+}
+
+void Window::pushMessage(const char *str) {
+ messages.push_back(str);
+
+ bool createdPacket = false;
+ Buffer packet;
+
+ for (int i = 0; i < core->clientCount; i++) {
+ if (core->clients[i]->isAuthed()) {
+ if (!createdPacket) {
+ packet.writeU32(id);
+ packet.writeStr(str);
+ createdPacket = true;
+ }
+
+ core->clients[i]->sendPacket(Packet::B2C_WINDOW_MESSAGE, packet);
+ }
+ }
+}
+
+
+
+
+StatusWindow::StatusWindow(IRCServer *_server) :
+ Window(_server->bouncer)
+{
+ server = _server;
+}
+
+const char *StatusWindow::getTitle() const {
+ return server->config.hostname;
+}
+
+int StatusWindow::getType() const {
+ return 1;
+}
+
+void StatusWindow::handleUserInput(const char *str) {
+ if (str[0] == '/') {
+ // moof
+ if (strcmp(str, "/connect") == 0) {
+ server->connect();
+ } else if (strcmp(str, "/disconnect") == 0) {
+ server->close();
+ } else if (strncmp(str, "/password ", 10) == 0) {
+ pushMessage("Password set.");
+
+ // This is ugly, ugh
+ strncpy(
+ server->config.password,
+ &str[10],
+ sizeof(server->config.password));
+ server->config.password[sizeof(server->config.password) - 1] = 0;
+ }
+ } else {
+ server->sendLine(str);
+ }
+}