Add BufferManager and fix all memory leaks.

This commit is contained in:
Kitteh 2021-06-06 12:30:15 +01:00
parent cb88f3c3b3
commit 82e3e8491c
12 changed files with 207 additions and 42 deletions

37
src/BufferManager.zig Normal file
View file

@ -0,0 +1,37 @@
const std = @import("std");
const BufferInfo = @import("./qtshit/types/UserType.zig").BufferInfo;
pub const BufferManager = struct {
allocator: *std.mem.Allocator,
buffers: std.ArrayList(BufferInfo),
pub fn addBufferInfo(s: *BufferManager, bufferInfo: BufferInfo) !void {
try s.buffers.append(BufferInfo{
.ID = bufferInfo.ID,
.NetworkID = bufferInfo.NetworkID,
.Type = bufferInfo.Type,
.Name = try s.allocator.dupe(u8, bufferInfo.Name),
});
}
pub fn getFirstByName(s: *BufferManager, name: []const u8) !?BufferInfo {
for (s.buffers.items) |item| {
if (std.mem.eql(u8, item.Name, name)) {
return item;
}
}
return null;
}
pub fn deinit(s: *BufferManager, ) void {
for (s.buffers.items) |item| {
s.allocator.free(item.Name);
}
s.buffers.deinit();
}
};
pub fn initBufferManager(allocator: *std.mem.Allocator) BufferManager {
return BufferManager{
.allocator = allocator,
.buffers = std.ArrayList(BufferInfo).init(allocator),
};
}

View file

@ -1,4 +1,7 @@
const std = @import("std");
const BufferManager = @import("./BufferManager.zig");
const read = @import("./qtshit/read.zig");
const write = @import("./qtshit/write.zig");
const range = @import("./qtshit/utils/RangeIter.zig").range;
@ -6,6 +9,8 @@ const QVariantType = @import("./qtshit/types/QVariant.zig").QVariant;
const prettyPrintQVariant = @import("./qtshit/utils/prettyPrintQVariant.zig").prettyPrintQVariant;
const freeQVariant = @import("./qtshit/utils/free/freeQVariant.zig").freeQVariant;
const QVariantMapToQVariantList = @import("./qtshit/utils/QVariantMapToQVariantList.zig").QVariantMapToQVariantList;
const UserType = @import("./qtshit/types/UserType.zig");
const tls = @import("./deps/iguanaTLS/src/main.zig");
fn dumpDebug(name: []const u8, list: std.ArrayList(u8)) !void {
@ -23,6 +28,7 @@ fn dumpDebug(name: []const u8, list: std.ArrayList(u8)) !void {
pub const Client = struct {
allocator: *std.mem.Allocator,
stream: *std.net.Stream,
bufferManager: BufferManager.BufferManager,
pub var tlsAllowed = !true;
pub var tlsConnected = !true;
@ -30,6 +36,11 @@ pub const Client = struct {
pub const TLSStream = tls.Client(std.net.Stream.Reader, std.net.Stream.Writer, tls.ciphersuites.all, false);
pub var tlsClient: TLSStream = undefined;
pub fn deinit(s: *Client) void {
s.bufferManager.deinit();
}
pub fn initTLS(s: *Client) !void {
if (!tlsConnected and tlsAllowed) {
var randBuf: [32]u8 = undefined;
@ -84,6 +95,7 @@ pub const Client = struct {
pub fn readFrame(s: *Client) !QVariantType {
try s.initTLS();
var data: std.ArrayList(u8) = undefined;
defer data.deinit();
if (tlsConnected) {
var reader = tlsClient.reader();
@ -112,18 +124,22 @@ pub const Client = struct {
}
pub fn quassel_init_packet(s: *Client) !void {
var data = std.ArrayList(u8).init(s.allocator);
defer data.deinit();
var map = std.StringHashMap(QVariantType).init(s.allocator);
defer map.deinit();
try map.put("MsgType", .{ .String = "ClientInit" });
try map.put("UseCompression", .{ .UInt = 0 });
try map.put("UseSsl", .{ .UInt = 1 });
try map.put("ProtocolVersion", .{ .UInt = 10 });
try map.put("ClientVersion", .{ .String = "0.1 (quasselclient)" });
try map.put("ClientDate", .{ .String = "Wed, 02 Jun 2021 17:30:30 +0100" });
try map.put("Features", .{ .Int = 0x00008000 });
var featureList = std.ArrayList([]const u8).init(s.allocator);
defer featureList.deinit();
try featureList.append("LongTime");
try featureList.append("LongMessageID");
try featureList.append("SenderPrefixes");
@ -137,16 +153,18 @@ pub const Client = struct {
try s.writeFrame(data);
var varient = try s.readFrame();
prettyPrintQVariant(varient, 0);
tlsAllowed = varient.QVariantMap.get("SupportSsl").?.Byte == 1;
freeQVariant(varient, s.allocator);
var variant = try s.readFrame();
defer freeQVariant(variant, s.allocator);
tlsAllowed = variant.QVariantMap.get("SupportSsl").?.Byte == 1;
}
pub fn quassel_login(s: *Client, username: []const u8, password: []const u8) !void {
var data = std.ArrayList(u8).init(s.allocator);
defer data.deinit();
var map = std.StringHashMap(QVariantType).init(s.allocator);
defer map.deinit();
try map.put("MsgType", .{ .String = "ClientLogin" });
try map.put("User", .{ .String = username });
try map.put("Password", .{ .String = password });
@ -156,13 +174,61 @@ pub const Client = struct {
});
try s.writeFrame(data);
var loginResponse = try s.readFrame();
defer freeQVariant(loginResponse, s.allocator);
var loginResponseMap = loginResponse.QVariantMap;
if (loginResponseMap.get("MsgType")) |msgType| {
if (std.mem.eql(u8, msgType.String, "ClientLoginReject")) {
return error.InvalidLogin;
}
}
}
fn handle_session_init_packet(s: *Client, sessionState: std.StringHashMap(QVariantType)) !void {
for (sessionState.get("BufferInfos").?.QVariantList) |qvar| {
try s.bufferManager.addBufferInfo(qvar.UserType.BufferInfo);
}
}
pub fn read_quassel_packet(s: *Client) !void {
std.debug.print("\n\nQuassel Packet: \n", .{});
var varient = try s.readFrame();
prettyPrintQVariant(varient, 0);
freeQVariant(varient, s.allocator);
var variant = try s.readFrame();
defer freeQVariant(variant, s.allocator);
switch (variant) {
.QVariantMap => |map| {
if (map.get("MsgType")) |msgType| {
if (std.mem.eql(u8, msgType.String, "SessionInit")) {
var sessionState = map.get("SessionState").?.QVariantMap;
try s.handle_session_init_packet(sessionState);
}
}
},
else => {
std.debug.print("\n\n Unknown: Quassel Packet: \n", .{});
prettyPrintQVariant(variant, 0);
},
}
}
pub fn send_message(s: *Client, bufferInfo: UserType.BufferInfo, message: []const u8) !void {
var data = std.ArrayList(u8).init(s.allocator);
defer data.deinit();
var listItems = std.ArrayList(QVariantType).init(s.allocator);
defer listItems.deinit();
try listItems.append(.{ .Int = 2 });
try listItems.append(.{ .QByteArray = "2sendInput(BufferInfo,QString)" });
try listItems.append(.{ .UserType = .{ .BufferInfo = bufferInfo } });
try listItems.append(.{ .String = message });
try write.writeQVariant(data.writer(), s.allocator, .{
.QVariantList = listItems.items,
});
try s.writeFrame(data);
}
};
@ -170,5 +236,6 @@ pub fn initClient(allocator: *std.mem.Allocator, stream: *std.net.Stream) Client
return Client{
.allocator = allocator,
.stream = stream,
.bufferManager = BufferManager.initBufferManager(allocator),
};
}

View file

@ -1,30 +1,40 @@
const std = @import("std");
const read = @import("./qtshit/read.zig");
const DebugAllocator = @import("./debug_allocator.zig");
const write = @import("./qtshit/write.zig");
const initClient = @import("./client.zig").initClient;
pub fn main() !void {
const allocator = std.heap.page_allocator;
pub fn realMain(allocator: *std.mem.Allocator) !void {
var argIter = std.process.args();
_ = try argIter.next(allocator).?;
allocator.free(try argIter.next(allocator).?);
var host = try argIter.next(allocator).?;
defer allocator.free(host);
var port = try argIter.next(allocator).?;
defer allocator.free(port);
var portInt = try std.fmt.parseInt(u16, port, 10);
var username = try argIter.next(allocator).?;
defer allocator.free(username);
var password = try argIter.next(allocator).?;
defer allocator.free(password);
std.debug.print("host={s} port={d}\n", .{ host, portInt });
while (true) {
var sock = try std.net.tcpConnectToHost(allocator, host, portInt);
var client = initClient(allocator, &sock);
defer client.deinit();
try client.handshake();
try client.quassel_init_packet();
try client.quassel_login(username, password);
_ = try client.read_quassel_packet();
var bufferInfo = try client.bufferManager.getFirstByName("z_is_stimky");
try client.send_message(bufferInfo.?, "uwu, owo, uwu");
while (true) {
client.read_quassel_packet() catch |err| {
if (err == error.DecodeError) {
@ -32,11 +42,22 @@ pub fn main() !void {
} else if (err == error.EndOfStream) {
std.debug.print("EOS.\n", .{});
std.time.sleep(1000 * std.time.ns_per_ms);
continue;
return;
} else {
return err;
}
};
break;
}
}
pub fn main() !void {
var gpalloc = std.heap.GeneralPurposeAllocator(.{
.stack_trace_frames = 20,
}){};
defer std.debug.assert(!gpalloc.deinit());
const alloc = &gpalloc.allocator;
try realMain(alloc);
}

View file

@ -12,8 +12,9 @@ const readMessage = @import("./readMessage.zig").readMessage;
pub fn readUserType(reader: anytype, allocator: *std.mem.Allocator) !UserType {
var userTypeName = try readQByteArray(reader, allocator);
defer allocator.free(userTypeName);
userTypeName = userTypeName[0 .. userTypeName.len - 1];
//std.debug.print("read: readUserType name={s} \n", .{userTypeName});
if (std.mem.eql(u8, userTypeName, "BufferId")) {
return UserType{

View file

@ -7,7 +7,7 @@ pub const QVariant = union(enum) {
Short: u16,
Byte: u8,
String: []const u8,
QByteArray: []u8,
QByteArray: []const u8,
QStringList: [][]const u8,
QVariantList: []QVariant,
QVariantMap: std.StringHashMap(QVariant),

View file

@ -20,7 +20,7 @@ pub const BufferInfo = struct {
ID: i32,
NetworkID: i32,
Type: u16,
Name: []u8,
Name: []const u8,
};
pub const Message = struct {

View file

@ -1,5 +1,5 @@
const std = @import("std");
pub fn freeQByteArray(qbytearray: []u8, allocator: *std.mem.Allocator) void {
pub fn freeQByteArray(qbytearray: []const u8, allocator: *std.mem.Allocator) void {
allocator.free(qbytearray);
}

View file

@ -43,22 +43,27 @@ pub fn prettyPrintUserType(usertype: UserType, indentLevel: u64) void {
},
.IrcUser => |value| {
std.debug.print("IrcUser:\n", .{});
print_indent_level(indentLevel + 1);
prettyPrintQVariantMap(value, indentLevel + 1);
},
.IrcChannel => |value| {
std.debug.print("IrcChannel:\n", .{});
print_indent_level(indentLevel + 1);
prettyPrintQVariantMap(value, indentLevel + 1);
},
.Identity => |value| {
std.debug.print("Identity:\n", .{});
print_indent_level(indentLevel + 1);
prettyPrintQVariantMap(value, indentLevel + 1);
},
.NetworkInfo => |value| {
std.debug.print("NetworkInfo:\n", .{});
print_indent_level(indentLevel + 1);
prettyPrintQVariantMap(value, indentLevel + 1);
},
.NetworkServer => |value| {
std.debug.print("NetworkServer:\n", .{});
print_indent_level(indentLevel + 1);
prettyPrintQVariantMap(value, indentLevel + 1);
},
.BufferInfo => |value| {
@ -106,7 +111,6 @@ pub fn prettyPrintUserType(usertype: UserType, indentLevel: u64) void {
}
pub fn prettyPrintQVariantMap(qvariantmap: std.StringHashMap(QVariant), indentLevel: u64) void {
print_indent_level(indentLevel);
std.debug.print("QVariantMap:\n", .{});
var qMapIter = qvariantmap.iterator();
while (qMapIter.next()) |v| {

View file

@ -26,7 +26,7 @@ pub fn utf16BEToUtf8(allocator: *std.mem.Allocator, utf16: []u16) ![]u8 {
// Big Endian to Little Endian
var utf16LE = byteSwapArray(u16, &utf16BE);
var utf8 = try std.unicode.utf16leToUtf8AllocZ(allocator, utf16LE);
var utf8 = try std.unicode.utf16leToUtf8Alloc(allocator, utf16LE);
return utf8;
}

View file

@ -0,0 +1,19 @@
const std = @import("std");
const BufferInfo = @import("../../types/UserType.zig").BufferInfo;
const writeInt = @import("../writeInt.zig").writeInt;
const writeShort = @import("../writeShort.zig").writeShort;
const writeByte = @import("../writeByte.zig").writeByte;
const writeQByteArray = @import("../writeQByteArray.zig").writeQByteArray;
pub fn writeBufferInfo(writer: anytype, bufferInfo: BufferInfo) !void {
try writeQByteArray(writer, "BufferInfo\x00");
try writeInt(writer, bufferInfo.ID);
try writeInt(writer, bufferInfo.NetworkID);
try writeShort(writer, bufferInfo.Type);
// 4 undocumented bytes.
try writeByte(writer, 0x00);
try writeByte(writer, 0x00);
try writeByte(writer, 0x00);
try writeByte(writer, 0x00);
try writeQByteArray(writer, bufferInfo.Name);
}

View file

@ -0,0 +1,14 @@
const std = @import("std");
const UserType = @import("../../types/UserType.zig").UserType;
const writeBufferInfo = @import("./writeBufferInfo.zig").writeBufferInfo;
pub fn writeUserType(writer: anytype, allocator: *std.mem.Allocator, usertype: UserType) !void {
switch (usertype) {
.BufferInfo => |value| {
try writeBufferInfo(writer, value);
},
else => {
@panic("Unsupported!");
},
}
}

View file

@ -13,6 +13,8 @@ const writeQVariantList = @import("./writeQVariantList.zig").writeQVariantList;
const writeQVariantHeader = @import("./writeQVariantHeader.zig").writeQVariantHeader;
const writeQVariantMap = @import("./writeQVariantMap.zig").writeQVariantMap;
const writeQStringList = @import("./writeQStringList.zig").writeQStringList;
const writeUserType = @import("./usertypes/writeUserType.zig").writeUserType;
pub fn writeQVariant(writer: anytype, allocator: *std.mem.Allocator, variant: QVariantType) (@TypeOf(writer).Error || std.os.WriteError || error{OutOfMemory} || error{InvalidUtf8})!void {
try writeQVariantHeader(writer, try QVariantTypeID(variant));
@ -47,8 +49,8 @@ pub fn writeQVariant(writer: anytype, allocator: *std.mem.Allocator, variant: QV
.QDateTime => {
@panic("Can't write QDateTime");
},
.UserType => {
@panic("Can't write UserTypes");
.UserType => |out| {
try writeUserType(writer, allocator, out);
},
//else => {
// @panic("Unsupported!");