--- /dev/null
+package common
+
+import "core:crypto/ed25519"
+
+Public_Key_Bytes :: [32]u8
+
+Create_Room :: struct {
+ room_id: string,
+ public: bool,
+}
+
+Create_Group :: struct {
+ group_name: string,
+ members_public_keys: []Public_Key_Bytes,
+}
+
+Public_Message :: struct {
+ room_id: string,
+ content: string,
+}
+
+Private_Message :: struct {
+ room_id: string,
+ content: string,
+}
+
+Direct_Message :: struct {
+ receiver_public_key: ed25519.Public_Key,
+ encrypted_content: []u8,
+}
+
+Group_Message :: struct {
+ group_id: u32,
+ encrypted_contents: map[Public_Key_Bytes][]u8,
+}
+
+Block_User :: struct {
+ user_public_key: ed25519.Public_Key,
+}
+
+Client_Request_Content :: union {
+ // Client only.
+ Block_User,
+ Create_Room,
+ Create_Group,
+
+ // Common.
+ Public_Message,
+ Private_Message,
+ Direct_Message,
+ Group_Message,
+}
+
+Client_Request :: struct {
+ public_key: ed25519.Public_Key,
+ content: Client_Request_Content,
+ timestamp: u32,
+ signature: [32]u8,
+}
+
+client_request_parse :: proc(source: []u8) -> (Client_Request, bool) {
+ panic("TODO")
+}
+
+client_request_verify :: proc(request: Client_Request, expiration: Maybe(u32)) -> bool {
+ panic("TODO")
+}
+
+Server_Request :: union {
+ Public_Message,
+ Private_Message,
+ Direct_Message,
+ Group_Message,
+}
+
+send_server_request :: proc(request: Server_Request) {
+ panic("TODO")
+}
--- /dev/null
+package main
+
+import "base:runtime"
+
+import "core:fmt"
+import "core:log"
+import "core:net"
+import "core:sync"
+import "core:thread"
+
+import "../common"
+
+Handle_Client_Data :: struct {
+ clients_sockets: ^map[net.Endpoint]net.TCP_Socket,
+ clients_sockets_mutex: ^sync.Mutex,
+
+ client_socket: net.TCP_Socket,
+ client_endpoint: net.Endpoint,
+}
+
+handle_client :: proc(data: Handle_Client_Data) {
+ using data
+
+ client_endpoint_string := net.to_string(client_endpoint)
+
+ defer {
+ log.infof("Client %v disconnected.", client_endpoint_string)
+
+ sync.mutex_lock(clients_sockets_mutex)
+ defer sync.mutex_unlock(clients_sockets_mutex)
+
+ delete_key(clients_sockets, client_endpoint)
+ }
+
+ recv_buffer: [4_096]u8
+ send_buffer: [4_096]u8
+
+ response: []u8
+
+ for {
+ bytes_read, err := net.recv(client_socket, recv_buffer[:])
+
+ if err != nil {
+ log.errorf("Failed to receive message with error %v.", err)
+ return
+ }
+
+ if bytes_read == 0 {
+ break
+ }
+
+ bytes := recv_buffer[:bytes_read]
+
+ defer net.send(response)
+
+ request, ok := common.client_request_parse(bytes)
+
+ if !ok {
+ response = "Invalid request"
+ continue
+ }
+
+ if !common.client_request_verify(request, 1 * 60 * 60 * 1_000_000_000) {
+ response = "Invalid authentication"
+ continue
+ }
+
+ switch content in request.content {
+
+ }
+
+ fmt.printf("<%v> %v\n", client_endpoint_string, message)
+ }
+}
+
+main :: proc() {
+ context.logger = log.create_console_logger()
+ defer log.destroy_console_logger(context.logger)
+
+ server_endpoint := net.Endpoint{
+ address = net.IP4_Address{ 127, 0, 0, 1 },
+ port = 3000,
+ }
+
+ server_socket, err1 := net.listen_tcp(server_endpoint)
+
+ if err1 != nil {
+ log.errorf("Failed to open server with error %v.", err1)
+ return
+ }
+
+ defer net.close(server_socket)
+
+ log.infof("Server open on port %v.", server_endpoint.port)
+
+ clients_sockets := make(map[net.Endpoint]net.TCP_Socket)
+
+ defer {
+ for _, socket in clients_sockets {
+ net.close(socket)
+ }
+
+ delete(clients_sockets)
+ }
+
+ clients_sockets_mutex: sync.Mutex
+
+ for {
+ client_socket, client_endpoint, err2 := net.accept_tcp(server_socket)
+
+ if err2 != nil {
+ log.errorf("Failed to accept client with error %v.", err2)
+ continue
+ }
+
+ client_endpoint_string := net.to_string(client_endpoint)
+
+ log.infof("Accepted client from %v.", client_endpoint_string)
+
+ {
+ sync.mutex_lock(&clients_sockets_mutex)
+ defer sync.mutex_unlock(&clients_sockets_mutex)
+
+ clients_sockets[client_endpoint] = client_socket
+ }
+
+ client_handle_data := Handle_Client_Data{
+ clients_sockets = &clients_sockets,
+ clients_sockets_mutex = &clients_sockets_mutex,
+
+ client_socket = client_socket,
+ client_endpoint = client_endpoint,
+ }
+
+ client_thread := thread.create_and_start_with_poly_data(
+ client_handle_data,
+ handle_client,
+ runtime.Context{
+ allocator = context.allocator,
+ logger = context.logger,
+ },
+ )
+ }
+}