Serenity Operating System
1/*
2 * Copyright (c) 2021, Dex♪ <dexes.ttp@gmail.com>
3 *
4 * SPDX-License-Identifier: BSD-2-Clause
5 */
6
7#include <AK/Base64.h>
8#include <AK/Format.h>
9#include <AK/URL.h>
10#include <LibCore/ArgsParser.h>
11#include <LibCore/EventLoop.h>
12#include <LibCore/System.h>
13#include <LibLine/Editor.h>
14#include <LibMain/Main.h>
15#include <LibProtocol/WebSocket.h>
16#include <LibProtocol/WebSocketClient.h>
17
18ErrorOr<int> serenity_main(Main::Arguments arguments)
19{
20 TRY(Core::System::pledge("stdio unix inet accept rpath wpath cpath fattr tty sigaction"));
21
22 Core::ArgsParser args_parser;
23
24 DeprecatedString origin;
25 DeprecatedString url_string;
26
27 args_parser.add_positional_argument(url_string, "URL to connect to", "url", Core::ArgsParser::Required::Yes);
28 args_parser.add_option(origin, "URL to use as origin", "origin", 'o', "origin");
29
30 args_parser.parse(arguments);
31
32 URL url(url_string);
33
34 if (!url.is_valid()) {
35 warnln("The given URL is not valid");
36 return 1;
37 }
38
39 Core::EventLoop loop;
40
41 auto maybe_websocket_client = Protocol::WebSocketClient::try_create();
42 if (maybe_websocket_client.is_error()) {
43 warnln("Failed to connect to the websocket server: {}\n", maybe_websocket_client.error());
44 return maybe_websocket_client.release_error();
45 }
46 auto websocket_client = maybe_websocket_client.release_value();
47
48 RefPtr<Line::Editor> editor = Line::Editor::construct();
49 bool should_quit = false;
50 auto socket = websocket_client->connect(url, origin);
51 if (!socket) {
52 warnln("Failed to start socket for '{}'\n", url);
53 return 1;
54 }
55 socket->on_open = [&]() {
56 outln("[WebSocket opened]"sv);
57 };
58 socket->on_error = [&](auto error) {
59 outln("[WebSocket Error : {}]", (unsigned)error);
60 };
61 socket->on_message = [&](auto message) {
62 if (!message.is_text) {
63 outln("[Received binary data : {} bytes]", message.data.size());
64 return;
65 }
66 outln("[Received utf8 text] {}", DeprecatedString(ReadonlyBytes(message.data)));
67 };
68 socket->on_close = [&](auto code, auto message, bool was_clean) {
69 outln("[Server {} closed connection : '{}' (code {})]",
70 was_clean ? "cleanly" : "dirtily",
71 message,
72 code);
73 should_quit = true;
74 Core::EventLoop::current().quit(0);
75 };
76
77 TRY(Core::System::pledge("stdio unix inet accept rpath wpath tty sigaction"));
78
79 TRY(Core::System::unveil(nullptr, nullptr));
80
81 outln("Started server. Commands :");
82 outln("- '<text>' send the text as message");
83 outln("- '.text <data>' send the text as message");
84 outln("- '.base64 <data>' send the binary data from a base64-encoded string as message");
85 outln("- '.exit' Ask to exit the server");
86 outln("- '.forceexit' Exit the server");
87 while (!should_quit) {
88 auto line_or_error = editor->get_line(">");
89 if (line_or_error.is_error()) {
90 continue;
91 }
92 auto line = line_or_error.value();
93 if (line.is_empty())
94 continue;
95
96 if (line.starts_with('.')) {
97 if (line.starts_with(".text "sv)) {
98 editor->add_to_history(line);
99 if (socket->ready_state() != Protocol::WebSocket::ReadyState::Open) {
100 outln("Could not send message : socket is not open.");
101 continue;
102 }
103 socket->send(line.substring(6));
104 continue;
105 }
106 if (line.starts_with(".base64 "sv)) {
107 editor->add_to_history(line);
108 if (socket->ready_state() != Protocol::WebSocket::ReadyState::Open) {
109 outln("Could not send message : socket is not open.");
110 continue;
111 }
112 auto base64_data = line.substring(8);
113 auto buffer = decode_base64(base64_data);
114 if (buffer.is_error()) {
115 outln("Could not send message : {}", buffer.error().string_literal());
116 } else {
117 socket->send(buffer.value(), false);
118 }
119 continue;
120 }
121 if (line == ".exit") {
122 editor->add_to_history(line);
123 if (socket->ready_state() != Protocol::WebSocket::ReadyState::Open) {
124 outln("Socket is not open. Exiting.");
125 should_quit = true;
126 continue;
127 }
128 socket->close();
129 continue;
130 }
131 if (line == ".forceexit") {
132 editor->add_to_history(line);
133 if (socket->ready_state() == Protocol::WebSocket::ReadyState::Open)
134 socket->close();
135 return 1;
136 }
137 outln("Unknown command : {}", line);
138 continue;
139 }
140 editor->add_to_history(line);
141 if (socket->ready_state() != Protocol::WebSocket::ReadyState::Open) {
142 outln("Could not send message : socket is not open.");
143 continue;
144 }
145 socket->send(line);
146 }
147
148 return 0;
149}