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 void join_channel(const String&);
60 void part_channel(const String&);
61 void change_nick(const String&);
62
63 bool is_nick_prefix(char) const;
64
65 IRCWindow* current_window() { return aid_get_active_window(); }
66 const IRCWindow* current_window() const { return aid_get_active_window(); }
67
68 Function<void()> on_disconnect;
69 Function<void()> on_server_message;
70 Function<void(const String&)> on_nickname_changed;
71 Function<void(IRCChannel&)> on_part_from_channel;
72
73 Function<NonnullRefPtr<IRCWindow>(void*, IRCWindow::Type, const String&)> aid_create_window;
74 Function<IRCWindow*()> aid_get_active_window;
75 Function<void()> aid_update_window_list;
76
77 void register_subwindow(IRCWindow&);
78 void unregister_subwindow(IRCWindow&);
79
80 IRCWindowListModel* client_window_list_model() { return m_client_window_list_model.ptr(); }
81 const IRCWindowListModel* client_window_list_model() const { return m_client_window_list_model.ptr(); }
82
83 int window_count() const { return m_windows.size(); }
84 const IRCWindow& window_at(int index) const { return *m_windows.at(index); }
85 IRCWindow& window_at(int index) { return *m_windows.at(index); }
86
87 size_t window_index(const IRCWindow& window) const
88 {
89 for (size_t i = 0; i < m_windows.size(); ++i) {
90 if (m_windows[i] == &window)
91 return i;
92 }
93 ASSERT_NOT_REACHED();
94 }
95
96 void did_part_from_channel(Badge<IRCChannel>, IRCChannel&);
97
98 void handle_user_input_in_channel(const String& channel_name, const String&);
99 void handle_user_input_in_query(const String& query_name, const String&);
100 void handle_user_input_in_server(const String&);
101
102 void handle_whois_action(const String&);
103 void handle_open_query_action(const String&);
104 void handle_close_query_action(const String&);
105 void handle_join_action(const String&);
106 void handle_part_action(const String&);
107 void handle_change_nick_action(const String&);
108
109 IRCQuery* query_with_name(const String&);
110 IRCQuery& ensure_query(const String& name);
111 IRCChannel& ensure_channel(const String& name);
112
113 void add_server_message(const String&, Color = Color::Black);
114
115private:
116 IRCClient();
117
118 struct Message {
119 String prefix;
120 String command;
121 Vector<String> arguments;
122 };
123
124 enum class PrivmsgOrNotice {
125 Privmsg,
126 Notice,
127 };
128
129 void receive_from_server();
130 void send(const String&);
131 void send_user();
132 void send_nick();
133 void send_pong(const String& server);
134 void send_privmsg(const String& target, const String&);
135 void send_notice(const String& target, const String&);
136 void send_whois(const String&);
137 void process_line(ByteBuffer&&);
138 void handle_join(const Message&);
139 void handle_part(const Message&);
140 void handle_ping(const Message&);
141 void handle_topic(const Message&);
142 void handle_rpl_topic(const Message&);
143 void handle_rpl_whoisuser(const Message&);
144 void handle_rpl_whoisserver(const Message&);
145 void handle_rpl_whoisoperator(const Message&);
146 void handle_rpl_whoisidle(const Message&);
147 void handle_rpl_endofwhois(const Message&);
148 void handle_rpl_whoischannels(const Message&);
149 void handle_rpl_topicwhotime(const Message&);
150 void handle_rpl_endofnames(const Message&);
151 void handle_rpl_namreply(const Message&);
152 void handle_privmsg_or_notice(const Message&, PrivmsgOrNotice);
153 void handle_nick(const Message&);
154 void handle(const Message&);
155 void handle_user_command(const String&);
156 void handle_ctcp_request(const StringView& peer, const StringView& payload);
157 void handle_ctcp_response(const StringView& peer, const StringView& payload);
158 void send_ctcp_response(const StringView& peer, const StringView& payload);
159
160 void on_socket_connected();
161
162 String m_hostname;
163 int m_port { 6667 };
164
165 RefPtr<Core::TCPSocket> m_socket;
166
167 String m_nickname;
168 RefPtr<Core::Notifier> m_notifier;
169 HashMap<String, RefPtr<IRCChannel>, CaseInsensitiveStringTraits> m_channels;
170 HashMap<String, RefPtr<IRCQuery>, CaseInsensitiveStringTraits> m_queries;
171
172 Vector<IRCWindow*> m_windows;
173
174 IRCWindow* m_server_subwindow { nullptr };
175
176 NonnullRefPtr<IRCWindowListModel> m_client_window_list_model;
177 NonnullRefPtr<IRCLogBuffer> m_log;
178 NonnullRefPtr<Core::ConfigFile> m_config;
179};