Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#pragma once
28
29#include "IRCLogBuffer.h"
30#include "IRCWindow.h"
31#include <AK/CircularQueue.h>
32#include <AK/Function.h>
33#include <AK/HashMap.h>
34#include <AK/String.h>
35#include <LibCore/ConfigFile.h>
36#include <LibCore/TCPSocket.h>
37
38class IRCChannel;
39class IRCQuery;
40class IRCWindowListModel;
41
42class IRCClient final : public Core::Object {
43 C_OBJECT(IRCClient)
44 friend class IRCChannel;
45 friend class IRCQuery;
46
47public:
48 virtual ~IRCClient() override;
49
50 void set_server(const String& hostname, int port = 6667);
51
52 bool connect();
53
54 String hostname() const { return m_hostname; }
55 int port() const { return m_port; }
56
57 String nickname() const { return m_nickname; }
58
59 String ctcp_version_reply() const { return m_ctcp_version_reply; }
60 String ctcp_userinfo_reply() const { return m_ctcp_userinfo_reply; }
61 String ctcp_finger_reply() const { return m_ctcp_finger_reply; }
62
63 void join_channel(const String&);
64 void part_channel(const String&);
65 void change_nick(const String&);
66
67 static bool is_nick_prefix(char);
68 static bool is_channel_prefix(char);
69 String nick_without_prefix(const String& nick);
70
71 IRCWindow* current_window() { return aid_get_active_window(); }
72 const IRCWindow* current_window() const { return aid_get_active_window(); }
73
74 Function<void()> on_disconnect;
75 Function<void()> on_server_message;
76 Function<void(const String&)> on_nickname_changed;
77 Function<void(IRCChannel&)> on_part_from_channel;
78
79 Function<NonnullRefPtr<IRCWindow>(void*, IRCWindow::Type, const String&)> aid_create_window;
80 Function<IRCWindow*()> aid_get_active_window;
81 Function<void()> aid_update_window_list;
82
83 void register_subwindow(IRCWindow&);
84 void unregister_subwindow(IRCWindow&);
85
86 IRCWindowListModel* client_window_list_model() { return m_client_window_list_model.ptr(); }
87 const IRCWindowListModel* client_window_list_model() const { return m_client_window_list_model.ptr(); }
88
89 int window_count() const { return m_windows.size(); }
90 const IRCWindow& window_at(int index) const { return *m_windows.at(index); }
91 IRCWindow& window_at(int index) { return *m_windows.at(index); }
92
93 size_t window_index(const IRCWindow& window) const
94 {
95 for (size_t i = 0; i < m_windows.size(); ++i) {
96 if (m_windows[i] == &window)
97 return i;
98 }
99 ASSERT_NOT_REACHED();
100 }
101
102 void did_part_from_channel(Badge<IRCChannel>, IRCChannel&);
103
104 void handle_user_input_in_channel(const String& channel_name, const String&);
105 void handle_user_input_in_query(const String& query_name, const String&);
106 void handle_user_input_in_server(const String&);
107
108 void handle_list_channels_action();
109 void handle_whois_action(const String& nick);
110 void handle_open_query_action(const String&);
111 void handle_close_query_action(const String&);
112 void handle_join_action(const String& channel_name);
113 void handle_part_action(const String& channel_name);
114 void handle_cycle_channel_action(const String& channel_name);
115 void handle_change_nick_action(const String& nick);
116 void handle_change_topic_action(const String& channel_name, const String&);
117 void handle_invite_user_action(const String& channel_name, const String& nick);
118 void handle_banlist_action(const String& channel_name);
119 void handle_voice_user_action(const String& channel_name, const String& nick);
120 void handle_devoice_user_action(const String& channel_name, const String& nick);
121 void handle_hop_user_action(const String& channel_name, const String& nick);
122 void handle_dehop_user_action(const String& channel_name, const String& nick);
123 void handle_op_user_action(const String& channel_name, const String& nick);
124 void handle_deop_user_action(const String& channel_name, const String& nick);
125 void handle_kick_user_action(const String& channel_name, const String& nick, const String&);
126
127 IRCQuery* query_with_name(const String&);
128 IRCQuery& ensure_query(const String& name);
129 IRCChannel& ensure_channel(const String& name);
130
131 void add_server_message(const String&, Color = Color::Black);
132
133private:
134 IRCClient();
135
136 struct Message {
137 String prefix;
138 String command;
139 Vector<String> arguments;
140 };
141
142 enum class PrivmsgOrNotice {
143 Privmsg,
144 Notice,
145 };
146
147 void receive_from_server();
148 void send(const String&);
149 void send_user();
150 void send_nick();
151 void send_pong(const String& server);
152 void send_privmsg(const String& target, const String&);
153 void send_notice(const String& target, const String&);
154 void send_topic(const String& channel_name, const String&);
155 void send_invite(const String& channel_name, const String& nick);
156 void send_banlist(const String& channel_name);
157 void send_voice_user(const String& channel_name, const String& nick);
158 void send_devoice_user(const String& channel_name, const String& nick);
159 void send_hop_user(const String& channel_name, const String& nick);
160 void send_dehop_user(const String& channel_name, const String& nick);
161 void send_op_user(const String& channel_name, const String& nick);
162 void send_deop_user(const String& channel_name, const String& nick);
163 void send_kick(const String& channel_name, const String& nick, const String&);
164 void send_list();
165 void send_whois(const String&);
166 void process_line(ByteBuffer&&);
167 void handle_join(const Message&);
168 void handle_part(const Message&);
169 void handle_quit(const Message&);
170 void handle_ping(const Message&);
171 void handle_topic(const Message&);
172 void handle_rpl_topic(const Message&);
173 void handle_rpl_whoisuser(const Message&);
174 void handle_rpl_whoisserver(const Message&);
175 void handle_rpl_whoisoperator(const Message&);
176 void handle_rpl_whoisidle(const Message&);
177 void handle_rpl_endofwho(const Message&);
178 void handle_rpl_endofwhois(const Message&);
179 void handle_rpl_endofwhowas(const Message&);
180 void handle_rpl_endofmotd(const Message&);
181 void handle_rpl_whoischannels(const Message&);
182 void handle_rpl_topicwhotime(const Message&);
183 void handle_rpl_endofnames(const Message&);
184 void handle_rpl_endofbanlist(const Message&);
185 void handle_rpl_namreply(const Message&);
186 void handle_rpl_banlist(const Message&);
187 void handle_err_nosuchnick(const Message&);
188 void handle_err_unknowncommand(const Message&);
189 void handle_err_nicknameinuse(const Message&);
190 void handle_privmsg_or_notice(const Message&, PrivmsgOrNotice);
191 void handle_nick(const Message&);
192 void handle(const Message&);
193 void handle_user_command(const String&);
194 void handle_ctcp_request(const StringView& peer, const StringView& payload);
195 void handle_ctcp_response(const StringView& peer, const StringView& payload);
196 void send_ctcp_response(const StringView& peer, const StringView& payload);
197
198 void on_socket_connected();
199
200 String m_hostname;
201 int m_port { 6667 };
202
203 RefPtr<Core::TCPSocket> m_socket;
204
205 String m_nickname;
206 RefPtr<Core::Notifier> m_notifier;
207 HashMap<String, RefPtr<IRCChannel>, CaseInsensitiveStringTraits> m_channels;
208 HashMap<String, RefPtr<IRCQuery>, CaseInsensitiveStringTraits> m_queries;
209
210 String m_ctcp_version_reply;
211 String m_ctcp_userinfo_reply;
212 String m_ctcp_finger_reply;
213
214 Vector<IRCWindow*> m_windows;
215
216 IRCWindow* m_server_subwindow { nullptr };
217
218 NonnullRefPtr<IRCWindowListModel> m_client_window_list_model;
219 NonnullRefPtr<IRCLogBuffer> m_log;
220 NonnullRefPtr<Core::ConfigFile> m_config;
221};