From c9151745631a45e6511eab6ec41308414be8757b Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Tue, 24 Aug 2021 16:19:08 +0200 Subject: [PATCH] Userland: Remove IRC Client The IRC Client application made some sense while our main communication hub was an IRC channel. Now that we've moved on, IRC is just a random protocol with no particular relevance to this project. This also has the benefit of removing one major client of the single- process Web::InProcessWebView class. --- AK/Debug.h.in | 4 - Base/etc/shellrc | 1 - Base/home/anon/.config/IRCClient.ini | 20 - Base/home/anon/.config/LaunchServer.ini | 1 - Base/res/apps/IRCClient.af | 7 - Base/res/icons/16x16/app-irc-client.png | Bin 197 -> 0 bytes Base/res/icons/16x16/irc-close-query.png | Bin 470 -> 0 bytes Base/res/icons/16x16/irc-invite.png | Bin 422 -> 0 bytes Base/res/icons/16x16/irc-join.png | Bin 424 -> 0 bytes Base/res/icons/16x16/irc-list.png | Bin 436 -> 0 bytes Base/res/icons/16x16/irc-nick.png | Bin 470 -> 0 bytes Base/res/icons/16x16/irc-open-query.png | Bin 461 -> 0 bytes Base/res/icons/16x16/irc-part.png | Bin 516 -> 0 bytes Base/res/icons/16x16/irc-topic.png | Bin 576 -> 0 bytes Base/res/icons/16x16/irc-whois.png | Bin 485 -> 0 bytes Base/res/icons/32x32/app-irc-client.png | Bin 353 -> 0 bytes Meta/CMake/all_the_debug_macros.cmake | 1 - README.md | 1 - Userland/Applications/CMakeLists.txt | 1 - .../Applications/IRCClient/CMakeLists.txt | 19 - .../Applications/IRCClient/IRCAppWindow.cpp | 352 ----- .../Applications/IRCClient/IRCAppWindow.h | 56 - .../Applications/IRCClient/IRCChannel.cpp | 123 -- Userland/Applications/IRCClient/IRCChannel.h | 74 -- .../IRCClient/IRCChannelMemberListModel.cpp | 56 - .../IRCClient/IRCChannelMemberListModel.h | 32 - Userland/Applications/IRCClient/IRCClient.cpp | 1164 ----------------- Userland/Applications/IRCClient/IRCClient.h | 216 --- .../Applications/IRCClient/IRCLogBuffer.cpp | 76 -- .../Applications/IRCClient/IRCLogBuffer.h | 38 - Userland/Applications/IRCClient/IRCQuery.cpp | 44 - Userland/Applications/IRCClient/IRCQuery.h | 44 - Userland/Applications/IRCClient/IRCWindow.cpp | 258 ---- Userland/Applications/IRCClient/IRCWindow.h | 62 - .../IRCClient/IRCWindowListModel.cpp | 66 - .../IRCClient/IRCWindowListModel.h | 33 - Userland/Applications/IRCClient/main.cpp | 85 -- .../Demos/WidgetGallery/GalleryWidget.cpp | 2 +- 38 files changed, 1 insertion(+), 2835 deletions(-) delete mode 100644 Base/home/anon/.config/IRCClient.ini delete mode 100644 Base/res/apps/IRCClient.af delete mode 100644 Base/res/icons/16x16/app-irc-client.png delete mode 100644 Base/res/icons/16x16/irc-close-query.png delete mode 100644 Base/res/icons/16x16/irc-invite.png delete mode 100644 Base/res/icons/16x16/irc-join.png delete mode 100644 Base/res/icons/16x16/irc-list.png delete mode 100644 Base/res/icons/16x16/irc-nick.png delete mode 100644 Base/res/icons/16x16/irc-open-query.png delete mode 100644 Base/res/icons/16x16/irc-part.png delete mode 100644 Base/res/icons/16x16/irc-topic.png delete mode 100644 Base/res/icons/16x16/irc-whois.png delete mode 100644 Base/res/icons/32x32/app-irc-client.png delete mode 100644 Userland/Applications/IRCClient/CMakeLists.txt delete mode 100644 Userland/Applications/IRCClient/IRCAppWindow.cpp delete mode 100644 Userland/Applications/IRCClient/IRCAppWindow.h delete mode 100644 Userland/Applications/IRCClient/IRCChannel.cpp delete mode 100644 Userland/Applications/IRCClient/IRCChannel.h delete mode 100644 Userland/Applications/IRCClient/IRCChannelMemberListModel.cpp delete mode 100644 Userland/Applications/IRCClient/IRCChannelMemberListModel.h delete mode 100644 Userland/Applications/IRCClient/IRCClient.cpp delete mode 100644 Userland/Applications/IRCClient/IRCClient.h delete mode 100644 Userland/Applications/IRCClient/IRCLogBuffer.cpp delete mode 100644 Userland/Applications/IRCClient/IRCLogBuffer.h delete mode 100644 Userland/Applications/IRCClient/IRCQuery.cpp delete mode 100644 Userland/Applications/IRCClient/IRCQuery.h delete mode 100644 Userland/Applications/IRCClient/IRCWindow.cpp delete mode 100644 Userland/Applications/IRCClient/IRCWindow.h delete mode 100644 Userland/Applications/IRCClient/IRCWindowListModel.cpp delete mode 100644 Userland/Applications/IRCClient/IRCWindowListModel.h delete mode 100644 Userland/Applications/IRCClient/main.cpp diff --git a/AK/Debug.h.in b/AK/Debug.h.in index 94b976fd29..bd51a04e95 100644 --- a/AK/Debug.h.in +++ b/AK/Debug.h.in @@ -218,10 +218,6 @@ #cmakedefine01 IMAGE_LOADER_DEBUG #endif -#ifndef IRC_DEBUG -#cmakedefine01 IRC_DEBUG -#endif - #ifndef ITEM_RECTS_DEBUG #cmakedefine01 ITEM_RECTS_DEBUG #endif diff --git a/Base/etc/shellrc b/Base/etc/shellrc index 0d9849171e..5700af0e0b 100644 --- a/Base/etc/shellrc +++ b/Base/etc/shellrc @@ -1,7 +1,6 @@ #!sh alias fm=FileManager -alias irc=IRCClient alias mag=Magnifier alias ms=Minesweeper alias sh=Shell diff --git a/Base/home/anon/.config/IRCClient.ini b/Base/home/anon/.config/IRCClient.ini deleted file mode 100644 index cc542335c6..0000000000 --- a/Base/home/anon/.config/IRCClient.ini +++ /dev/null @@ -1,20 +0,0 @@ -[User] -Nickname=anon_seren1ty - -[Connection] -Server=chat.freenode.net -Port=6667 -AutoJoinChannels= - -[CTCP] -VersionReply=IRC Client [x86] / Serenity OS -UserInfoReply=anon -FingerReply=anon - -[Messaging] -ShowJoinPartMessages=1 -ShowNickChangeMessages=1 - -[Notifications] -NotifyOnMessage=1 -NotifyOnMention=1 diff --git a/Base/home/anon/.config/LaunchServer.ini b/Base/home/anon/.config/LaunchServer.ini index 7c14d637a0..0c7910f136 100644 --- a/Base/home/anon/.config/LaunchServer.ini +++ b/Base/home/anon/.config/LaunchServer.ini @@ -28,4 +28,3 @@ directory=/bin/FileManager gemini=/bin/Browser http=/bin/Browser https=/bin/Browser -irc=/bin/IRCClient diff --git a/Base/res/apps/IRCClient.af b/Base/res/apps/IRCClient.af deleted file mode 100644 index 3b2ed41b50..0000000000 --- a/Base/res/apps/IRCClient.af +++ /dev/null @@ -1,7 +0,0 @@ -[App] -Name=IRC Client -Executable=/bin/IRCClient -Category=Internet - -[Launcher] -Protocols=irc diff --git a/Base/res/icons/16x16/app-irc-client.png b/Base/res/icons/16x16/app-irc-client.png deleted file mode 100644 index 82c1a5c27378685463af407c210ba2feddc680f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 197 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7dOTemLo7}wCoB*!P}=x^ z@&YmE5A}XWjrZ(YUgD8x+QK^X^Hbw8=Y2JGeU}r|ybd?8&VJCeQfH9dem&H{=^ZjLmqB^m5vL*@ zh2IlnKFEvPRlfRF|KvrAqUv4YiSZsqvZV|Ryi&FW*K0ohV_;xl@O1TaS?83{1ON;* BPZ|IK diff --git a/Base/res/icons/16x16/irc-close-query.png b/Base/res/icons/16x16/irc-close-query.png deleted file mode 100644 index ba2b405229d61a480f89fde7d27351ffb83998c8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 470 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^RNbCYGe8D3oWGWGJ|M`UZqI@`*DrFxq*#IEGl9?ww?t#}vrZVy_<^u#iI|IAF1@ zgUGvsq87s8Cv1zFMN7H7Ci!*--1F)Bo7mX;B(!dl{q1D`g6E>H5C84gIW2LjyZS?p z+2I)mH=eCt#nzqT{Gr5()yHCj@1z+%YUaU=Prj?%a1)#4sC4p==EApm2ZW|gydomB z%~GykJm}NHbK5*UedkQLD_XImd6UlKRjbq@Z*(r%wOhq+`Q@`$nyn|eOv;kXOWyAL zul(4@ik>MO9KDt-IcR)LS@~&YUus;P9pjC6-(vrz{e0snf5&4ZLxlb_*)5TF)q7{~ zzh96qwz}>9X7wKy7aZdBpY5rYx$*9s;Pr%Qsb6>UxCEbWWDZ-oh`(G|#PwmI-61Az z$14up>}gZ?S?`#evir7so!oXaZbp-~PL2b6Uh%}AKKYw_wRY^TO|`OjZgU>cKXv+F VQ<{ctBLf2igQu&X%Q~loCIE9GxH=O_9rZ?tm0}xQfY1s3=EPbt`Q~9`MJ5Nc_j?a zMX8A;sVNHOnI#zt?w-B@;f;La3=E8Xo-U3d7N?g^KG@rAAmBRx`jiK!!yd4OH7$;H zjPks7gL{h;>$4pm+HY^#7Tjc-P@ItCs1P{i&*>YU5hz(&GMY~J8Mfz%T+l!hehhcn7;44v7Kwdsj15?yhKeJHu4_O;hB9$#C@)4 z$b=b>I$Ualee*?oTGt31**94&Zi>Pv!T#Y zEmHgDnWh+@V`iU4j&DuXT)L(Hm{;!Xiq$EXb+*oDetzd*iA{rT{nXMob&NZh{%CaX V=#FgfXJBAp@O1TaS?83{1OS1WsfPdn diff --git a/Base/res/icons/16x16/irc-join.png b/Base/res/icons/16x16/irc-join.png deleted file mode 100644 index 3a8692780570fa2dd46ddab69b2a47950cf020b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 424 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^OtxRxtQw?hRVb zz`(#+;1OBOz@VoL!i*J5?aLS#7}!fZeO=ifGxLj>sm}WFdAk_ba}OH`)bf}z%|0+yTkpXE zolVW*6V*3{UF2iaVTx=BwF)N?IMjrMP*?v`1?%D2BZnQu2qWS)pPp5Q5 zGi7?#+%4$Z9(MZY)@ye%>>s2&jZ1&dtl`^xFj}wInwep4*)ox(+&T;v@(c>yhk6!^ zthIWhaCczCog;f@AR&RCY@*UlvDY>fqIp3usesQr+oXj~4 zhi5K({73IZ|C^cciB#OlnFu-~PdK>r3bZ a_7{wkwa&-*d}m-_VDNPHb6Mw<&;$StzNr%c diff --git a/Base/res/icons/16x16/irc-list.png b/Base/res/icons/16x16/irc-list.png deleted file mode 100644 index 05d0eb35cce96747c331c60d25d5823feb3b60f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 436 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^Rm@;DWu&Co?cG za29w(7BevDDT6R$#Zvn+1_lQ95>H=O_9rZ?tdb%ueo`I`3=EPbt`Q~9`MJ5Nc_j?a zMX8A;sVNHOnI#zt?w-B@;f;La3=E7io-U3d7N_?H8+x%Aiqsk=D>hyIVwNOzFs*R; zT;VsiYtr5)yuWi$_>8Vv(!zv?;)z>tYHd*HxcWo)ldzGWSm43?f1b4N`Ccc-_%A|B z=ggvsx|>S^u4`G&PFPqLwC>UwXL+G_x-;fHnc^LMFmS%aoTMwUS_~RZo0PS+E-(bl z;$mP+lt}Db!^g+f@bHAwu78V`9p}qY>1MgI^;3tps^1N5ZP}bzM}rJpbUt_NU;SU* z;4IIwnSXVTt?n)^=EPpEisB5OU5#jDi!#_L~8 z?X>&Foix`wn71#d=gqWvKfnAac=X`i$D{oMTBS`Xug+Grw%=u1pZ{La`o{gkX?suo i>B|@5aDB_m@IW{^Y=YePh$o=Fdh=m|29$h&NE21Ud50WCTK1djO%Mu<+L9UclWC&Q zan&G8R?QtuCYeQTGL7CB3R}Oj21$BrFLk?BsmaqJfBTVVbk7#!-&v#xet+fjcy_x$UbCTZYvfX=kTg>!%!R7f|W7%a*hS1QDJ0k?Hau)br_+B+- zg3{xUKl&C;U$8El!HMHUi^`dt83(H0Kl}VMvRy#K-fM~3g8~bN9qk-W98*#|XLbmc z{b7Fp_k)<8=8}$-87@5=H*@E$;LeQ%|W;$wNaU<3%mg z4)*W|J~55e2ROGaU+5wDP(;CX;y2r_t0pstnmg**+;okU4b-H98{ z=53dDS-EUV?&Qt4tdxH{6l`MGT$(X!*(cYPg0CiAP?&NV`_7#J8BJYD@< J);T3K0Ra3#+!FJ?!H*8Q9-T^_fJs<`zu zN{Hr9&HT)^@{IP4S?ZoTwXAEu@vhmEu(V@`%c4dFu7liB&m&UG?{rEF{Fyz|()js* z{^xHF*`Icq9aL&>GZQ!G~hA;7<@1-X%m-^n5 zTbImYI5Ya;;_CE`2l6EvN=$Z&#~1|Pa1xp{BPxcSb7lT@pE5~ByZP_dHkow96)<*O z|0b%iOw7FE{T&M~wN0+=8PY6u#wHu{oKz?MYS?JxeRbCanU|bu$GraD4`4Fe)OaY< zIU%XJWE0c(!lk7=x)z%6qH^+emrpe0eC-ck@02CUe13M*(z@UT!^#8sLO*yJ9#1{> zOuf~yr};*Y^SYqMtS3R|5n7RMYri)uV%4B}CIRqXynC^JV Rkb!}L!PC{xWt~$(695wP&ldmy diff --git a/Base/res/icons/16x16/irc-topic.png b/Base/res/icons/16x16/irc-topic.png deleted file mode 100644 index 56a8e08ebcbbef6fba6aab617fd0dff865cf6724..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!s7*pj^6T^Rm@;DWu&Co?cG za29w(7BevDDT6R$#Zvn+1_lQ95>H=O_9rZ?tbDQ+f@)O^3=EPbt`Q~9`MJ5Nc_j?a zMX8A;sVNHOnI#zt?w-B@;f;La3=E7bJY5_^EKV=IY@HDjD8TmM{gGach$T0+3TzSF zYbJJT))JFe7Q+dCmzD+zD6Lt_vPQ^pHVX^4e6OHNgl^Jer%N0sqck0NC~9c-?1+ru zc077~>xrEYPw6ohCd?>)_VN7VGY{=GpR}iJ5nF$Ao6qabvfE;=vp(qBVrSf#pOMV+ zx}v=~qI=zE*M>{?XTCkIS>wjZKEdU2U`@w1GpTKJnzq;#^KAdfy?&FIscu~V~!QA5y<`l^B7jW*b`oOdLh4z%&p-!7` z&N=S>#x>sm%J)Z49zA*_dBf|9Cbx+Ud!-7F&%aEusOZy<-&!E1&^=AE`79b`eeqnB`bG4^-n$- zU&u7wnPdJYo!#u~!>@l8alh>~ox5oFm1Bnv9WvE9dhfPk%SnMlDbbNU`^ViTY8g+rCq)+@0?e}Yppo9MooO#d}}=YCPN&)k1^X6@q#ry|#1x7%;- zD;!)}$Q!pdOhTCLcFkPB$-bL3D|W^(smYvB^AvIY=q~C|ceW`vMNv{^(wY5YZ7@_%Z``)(_Kc7O>Dx<|{Igb^y~}FTU7cgQzE8gtde_b8?E(!hmBen9j{&ut z?&|!osOc{~Y&-jtYH5PkA))j6B1g6Mrpf z{j63wFL~w?6VCfJ#}0PAiB>(bmV>>Z^L_c-^|nV;XQWDQvo@-_)5yHx_%i1;n;GXl zHQtodwzTmj)76PC>z@{0cbda1!>m#s&0oc;*1^}q(DZj*>h8!5`}6<*^LeE{fA+S! lt6yCD`6a5aQsNvx&!zXAw|*M^XJBAp@O1TaS?83{1ORtG!EFEl diff --git a/Base/res/icons/32x32/app-irc-client.png b/Base/res/icons/32x32/app-irc-client.png deleted file mode 100644 index 8a9abb8c1a3e0e04e577bd6faa6d7694d9fe923b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 353 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I4mJh`hT^KKFANNf8lEnWArXg@6C_xfc_cP= z{9V4OQYG_;ywLWUIk&Itd{VtBsB?p*vO?8!+n>|VpQl*PYL)TjUN-B&hF^ z{&_O^`pG`EV&k*_PxK`yURTi5%bWDI=Jz*I=bp?3n;))VX1l|vbLGtCD+LUXXSweC zFDW4*k)W!yrDSooh`}R)WR>M>makAb622|@^yMpS_Wl#r^cOhbDTn`ONX(W{tW23d`OW12@0*z=;{N - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCAppWindow.h" -#include "IRCChannel.h" -#include "IRCWindow.h" -#include "IRCWindowListModel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static IRCAppWindow* s_the; - -IRCAppWindow& IRCAppWindow::the() -{ - return *s_the; -} - -IRCAppWindow::IRCAppWindow(String server, int port) - : m_client(IRCClient::construct(server, port)) -{ - VERIFY(!s_the); - s_the = this; - - set_icon(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/app-irc-client.png")); - - update_title(); - resize(600, 400); - setup_actions(); - setup_menus(); - setup_widgets(); - - setup_client(); -} - -IRCAppWindow::~IRCAppWindow() -{ -} - -void IRCAppWindow::update_title() -{ - set_title(String::formatted("{}@{}:{} - IRC Client", m_client->nickname(), m_client->hostname(), m_client->port())); -} - -void IRCAppWindow::setup_client() -{ - m_client->aid_create_window = [this](void* owner, IRCWindow::Type type, const String& name) { - return create_window(owner, type, name); - }; - m_client->aid_get_active_window = [this] { - return static_cast(m_container->active_widget()); - }; - m_client->aid_update_window_list = [this] { - m_window_list->model()->invalidate(); - }; - m_client->on_nickname_changed = [this](const String&) { - update_title(); - }; - m_client->on_part_from_channel = [this](auto&) { - update_gui_actions(); - }; - - if (m_client->hostname().is_empty()) { - String value; - if (GUI::InputBox::show(this, value, "Enter server:", "Connect to server") == GUI::InputBox::ExecCancel) { - GUI::Application::the()->quit(); - return; - } - - m_client->set_server(value, 6667); - } - update_title(); - bool success = m_client->connect(); - VERIFY(success); -} - -void IRCAppWindow::setup_actions() -{ - m_join_action = GUI::Action::create("&Join Channel...", { Mod_Ctrl, Key_J }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-join.png"), [&](auto&) { - String value; - if (GUI::InputBox::show(this, value, "Enter channel name:", "Join Channel") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_join_action(value); - }); - - m_list_channels_action = GUI::Action::create("&List Channels", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-list.png"), [&](auto&) { - m_client->handle_list_channels_action(); - }); - - m_part_action = GUI::Action::create("&Part from Channel", { Mod_Ctrl, Key_P }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-part.png"), [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - m_client->handle_part_action(window->channel().name()); - }); - - m_whois_action = GUI::Action::create("&Whois User...", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-whois.png"), [&](auto&) { - String value; - if (GUI::InputBox::show(this, value, "Enter nickname:", "Whois User") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_whois_action(value); - }); - - m_open_query_action = GUI::Action::create("Open &Query...", { Mod_Ctrl, Key_O }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-open-query.png"), [&](auto&) { - String value; - if (GUI::InputBox::show(this, value, "Enter nickname:", "Open Query") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_open_query_action(value); - }); - - m_close_query_action = GUI::Action::create("&Close Query", { Mod_Ctrl, Key_D }, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-close-query.png"), [](auto&) { - outln("FIXME: Implement close-query action"); - }); - - m_change_nick_action = GUI::Action::create("Change &Nickname...", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-nick.png"), [this](auto&) { - String value; - if (GUI::InputBox::show(this, value, "Enter nickname:", "Change Nickname") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_change_nick_action(value); - }); - - m_change_topic_action = GUI::Action::create("Change &Topic...", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-topic.png"), [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter topic:", "Change Topic") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_change_topic_action(window->channel().name(), value); - }); - - m_invite_user_action = GUI::Action::create("&Invite User...", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-invite.png"), [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter nick:", "Invite User") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_invite_user_action(window->channel().name(), value); - }); - - m_banlist_action = GUI::Action::create("&Ban List", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - m_client->handle_banlist_action(window->channel().name()); - }); - - m_voice_user_action = GUI::Action::create("&Voice User...", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter nick:", "Voice User") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_voice_user_action(window->channel().name(), value); - }); - - m_devoice_user_action = GUI::Action::create("DeVoice User...", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter nick:", "DeVoice user") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_devoice_user_action(window->channel().name(), value); - }); - - m_hop_user_action = GUI::Action::create("Hop User", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter nick:", "Hop User") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_hop_user_action(window->channel().name(), value); - }); - - m_dehop_user_action = GUI::Action::create("DeHop User", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter nick:", "DeHop User") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_dehop_user_action(window->channel().name(), value); - }); - - m_op_user_action = GUI::Action::create("&Op User", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter nick:", "Op User") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_op_user_action(window->channel().name(), value); - }); - - m_deop_user_action = GUI::Action::create("DeOp user", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String value; - if (GUI::InputBox::show(this, value, "Enter nick:", "DeOp User") == GUI::InputBox::ExecOK && !value.is_empty()) - m_client->handle_deop_user_action(window->channel().name(), value); - }); - - m_kick_user_action = GUI::Action::create("&Kick User", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - String nick_value; - if (GUI::InputBox::show(this, nick_value, "Enter nick:", "Kick User") != GUI::InputBox::ExecOK || nick_value.is_empty()) - return; - String reason_value; - if (GUI::InputBox::show(this, reason_value, "Enter reason:", "Reason") == GUI::InputBox::ExecOK) - m_client->handle_kick_user_action(window->channel().name(), nick_value, reason_value.characters()); - }); - - m_cycle_channel_action = GUI::Action::create("C&ycle Channel", [this](auto&) { - auto* window = m_client->current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) { - return; - } - m_client->handle_cycle_channel_action(window->channel().name()); - }); -} - -void IRCAppWindow::setup_menus() -{ - auto& file_menu = add_menu("&File"); - file_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { - GUI::Application::the()->quit(); - })); - - auto& server_menu = add_menu("&Server"); - server_menu.add_action(*m_change_nick_action); - server_menu.add_separator(); - server_menu.add_action(*m_join_action); - server_menu.add_action(*m_list_channels_action); - server_menu.add_separator(); - server_menu.add_action(*m_whois_action); - server_menu.add_action(*m_open_query_action); - server_menu.add_action(*m_close_query_action); - - auto& channel_menu = add_menu("&Channel"); - channel_menu.add_action(*m_change_topic_action); - channel_menu.add_action(*m_invite_user_action); - channel_menu.add_action(*m_banlist_action); - - auto& channel_control_menu = channel_menu.add_submenu("Con&trol"); - channel_control_menu.add_action(*m_voice_user_action); - channel_control_menu.add_action(*m_devoice_user_action); - channel_control_menu.add_action(*m_hop_user_action); - channel_control_menu.add_action(*m_dehop_user_action); - channel_control_menu.add_action(*m_op_user_action); - channel_control_menu.add_action(*m_deop_user_action); - channel_control_menu.add_separator(); - channel_control_menu.add_action(*m_kick_user_action); - - channel_menu.add_separator(); - channel_menu.add_action(*m_cycle_channel_action); - channel_menu.add_action(*m_part_action); - - auto& help_menu = add_menu("&Help"); - help_menu.add_action(GUI::CommonActions::make_about_action("IRC Client", GUI::Icon::default_icon("app-irc-client"), this)); -} - -void IRCAppWindow::setup_widgets() -{ - auto& widget = set_main_widget(); - widget.set_fill_with_background_color(true); - widget.set_layout(); - widget.layout()->set_spacing(0); - - auto& toolbar_container = widget.add(); - auto& toolbar = toolbar_container.add(); - toolbar.set_has_frame(false); - toolbar.add_action(*m_change_nick_action); - toolbar.add_separator(); - toolbar.add_action(*m_join_action); - toolbar.add_action(*m_part_action); - toolbar.add_separator(); - toolbar.add_action(*m_whois_action); - toolbar.add_action(*m_open_query_action); - toolbar.add_action(*m_close_query_action); - - auto& outer_container = widget.add(); - outer_container.set_layout(); - outer_container.layout()->set_margins({ 0, 2, 2 }); - - auto& horizontal_container = outer_container.add(); - - m_window_list = horizontal_container.add(); - m_window_list->set_column_headers_visible(false); - m_window_list->set_alternating_row_colors(false); - m_window_list->set_model(m_client->client_window_list_model()); - m_window_list->set_activates_on_selection(true); - m_window_list->set_fixed_width(100); - m_window_list->on_activation = [this](auto& index) { - set_active_window(m_client->window_at(index.row())); - }; - - m_container = horizontal_container.add(); - m_container->on_active_widget_change = [this](auto*) { - update_gui_actions(); - }; - - create_window(&m_client, IRCWindow::Server, "Server"); -} - -void IRCAppWindow::set_active_window(IRCWindow& window) -{ - m_container->set_active_widget(&window); - window.clear_unread_count(); - auto index = m_window_list->model()->index(m_client->window_index(window)); - m_window_list->selection().set(index); -} - -void IRCAppWindow::update_gui_actions() -{ - auto* window = static_cast(m_container->active_widget()); - bool is_open_channel = window && window->type() == IRCWindow::Type::Channel && window->channel().is_open(); - m_change_topic_action->set_enabled(is_open_channel); - m_invite_user_action->set_enabled(is_open_channel); - m_banlist_action->set_enabled(is_open_channel); - m_voice_user_action->set_enabled(is_open_channel); - m_devoice_user_action->set_enabled(is_open_channel); - m_hop_user_action->set_enabled(is_open_channel); - m_dehop_user_action->set_enabled(is_open_channel); - m_op_user_action->set_enabled(is_open_channel); - m_deop_user_action->set_enabled(is_open_channel); - m_kick_user_action->set_enabled(is_open_channel); - m_cycle_channel_action->set_enabled(is_open_channel); - m_part_action->set_enabled(is_open_channel); -} - -NonnullRefPtr IRCAppWindow::create_window(void* owner, IRCWindow::Type type, const String& name) -{ - return m_container->add(m_client, owner, type, name); -} diff --git a/Userland/Applications/IRCClient/IRCAppWindow.h b/Userland/Applications/IRCClient/IRCAppWindow.h deleted file mode 100644 index 79be0a0538..0000000000 --- a/Userland/Applications/IRCClient/IRCAppWindow.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "IRCClient.h" -#include "IRCWindow.h" -#include -#include - -class IRCAppWindow : public GUI::Window { - C_OBJECT(IRCAppWindow); - -public: - virtual ~IRCAppWindow() override; - - static IRCAppWindow& the(); - - void set_active_window(IRCWindow&); - -private: - IRCAppWindow(String server, int port); - - void setup_client(); - void setup_actions(); - void setup_menus(); - void setup_widgets(); - void update_title(); - void update_gui_actions(); - - NonnullRefPtr create_window(void* owner, IRCWindow::Type, const String& name); - NonnullRefPtr m_client; - RefPtr m_container; - RefPtr m_window_list; - RefPtr m_join_action; - RefPtr m_list_channels_action; - RefPtr m_part_action; - RefPtr m_cycle_channel_action; - RefPtr m_whois_action; - RefPtr m_open_query_action; - RefPtr m_close_query_action; - RefPtr m_change_nick_action; - RefPtr m_change_topic_action; - RefPtr m_invite_user_action; - RefPtr m_banlist_action; - RefPtr m_voice_user_action; - RefPtr m_devoice_user_action; - RefPtr m_hop_user_action; - RefPtr m_dehop_user_action; - RefPtr m_op_user_action; - RefPtr m_deop_user_action; - RefPtr m_kick_user_action; -}; diff --git a/Userland/Applications/IRCClient/IRCChannel.cpp b/Userland/Applications/IRCClient/IRCChannel.cpp deleted file mode 100644 index 7606b05a1f..0000000000 --- a/Userland/Applications/IRCClient/IRCChannel.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCChannel.h" -#include "IRCChannelMemberListModel.h" -#include "IRCClient.h" - -IRCChannel::IRCChannel(IRCClient& client, const String& name) - : m_client(client) - , m_name(name) - , m_log(IRCLogBuffer::create()) - , m_member_model(IRCChannelMemberListModel::create(*this)) -{ - m_window = m_client.aid_create_window(this, IRCWindow::Channel, m_name); - m_window->set_log_buffer(*m_log); -} - -IRCChannel::~IRCChannel() -{ -} - -NonnullRefPtr IRCChannel::create(IRCClient& client, const String& name) -{ - return adopt_ref(*new IRCChannel(client, name)); -} - -void IRCChannel::add_member(const String& name, char prefix) -{ - for (auto& member : m_members) { - if (member.name == name) { - member.prefix = prefix; - return; - } - } - m_members.append({ name, prefix }); - m_member_model->invalidate(); -} - -void IRCChannel::remove_member(const String& name) -{ - m_members.remove_first_matching([&](auto& member) { return name == member.name; }); -} - -void IRCChannel::add_message(char prefix, const String& name, const String& text, Color color) -{ - log().add_message(prefix, name, text, color); - window().did_add_message(name, text); -} - -void IRCChannel::add_message(const String& text, Color color) -{ - log().add_message(text, color); - window().did_add_message(); -} - -void IRCChannel::say(const String& text) -{ - m_client.send_privmsg(m_name, text); - add_message(' ', m_client.nickname(), text); -} - -void IRCChannel::handle_join(const String& nick, const String& hostmask) -{ - if (nick == m_client.nickname()) { - m_open = true; - return; - } - add_member(nick, (char)0); - m_member_model->invalidate(); - if (m_client.show_join_part_messages()) - add_message(String::formatted("*** {} [{}] has joined {}", nick, hostmask, m_name), Color::MidGreen); -} - -void IRCChannel::handle_part(const String& nick, const String& hostmask) -{ - if (nick == m_client.nickname()) { - m_open = false; - m_members.clear(); - m_client.did_part_from_channel({}, *this); - } else { - remove_member(nick); - } - m_member_model->invalidate(); - if (m_client.show_join_part_messages()) - add_message(String::formatted("*** {} [{}] has parted from {}", nick, hostmask, m_name), Color::MidGreen); -} - -void IRCChannel::handle_quit(const String& nick, const String& hostmask, const String& message) -{ - if (nick == m_client.nickname()) { - m_open = false; - m_members.clear(); - m_client.did_part_from_channel({}, *this); - } else { - remove_member(nick); - } - m_member_model->invalidate(); - add_message(String::formatted("*** {} [{}] has quit ({})", nick, hostmask, message), Color::MidGreen); -} - -void IRCChannel::handle_topic(const String& nick, const String& topic) -{ - if (nick.is_null()) - add_message(String::formatted("*** Topic is \"{}\"", topic), Color::MidBlue); - else - add_message(String::formatted("*** {} set topic to \"{}\"", nick, topic), Color::MidBlue); -} - -void IRCChannel::notify_nick_changed(const String& old_nick, const String& new_nick) -{ - for (auto& member : m_members) { - if (member.name == old_nick) { - member.name = new_nick; - m_member_model->invalidate(); - if (m_client.show_nick_change_messages()) - add_message(String::formatted("~ {} changed nickname to {}", old_nick, new_nick), Color::MidMagenta); - return; - } - } -} diff --git a/Userland/Applications/IRCClient/IRCChannel.h b/Userland/Applications/IRCClient/IRCChannel.h deleted file mode 100644 index aebea43862..0000000000 --- a/Userland/Applications/IRCClient/IRCChannel.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "IRCLogBuffer.h" -#include -#include -#include -#include - -class IRCClient; -class IRCChannelMemberListModel; -class IRCWindow; - -class IRCChannel : public RefCounted { -public: - static NonnullRefPtr create(IRCClient&, const String&); - ~IRCChannel(); - - bool is_open() const { return m_open; } - void set_open(bool b) { m_open = b; } - - String name() const { return m_name; } - - void add_member(const String& name, char prefix); - void remove_member(const String& name); - - void add_message(char prefix, const String& name, const String& text, Color = Color::Black); - void add_message(const String& text, Color = Color::Black); - - void say(const String&); - - const IRCLogBuffer& log() const { return *m_log; } - IRCLogBuffer& log() { return *m_log; } - - IRCChannelMemberListModel* member_model() { return m_member_model.ptr(); } - const IRCChannelMemberListModel* member_model() const { return m_member_model.ptr(); } - - int member_count() const { return m_members.size(); } - String member_at(int i) { return m_members[i].name; } - - void handle_join(const String& nick, const String& hostmask); - void handle_part(const String& nick, const String& hostmask); - void handle_quit(const String& nick, const String& hostmask, const String& message); - void handle_topic(const String& nick, const String& topic); - - IRCWindow& window() { return *m_window; } - const IRCWindow& window() const { return *m_window; } - - String topic() const { return m_topic; } - - void notify_nick_changed(const String& old_nick, const String& new_nick); - -private: - IRCChannel(IRCClient&, const String&); - - IRCClient& m_client; - String m_name; - String m_topic; - struct Member { - String name; - char prefix { 0 }; - }; - Vector m_members; - bool m_open { false }; - - NonnullRefPtr m_log; - NonnullRefPtr m_member_model; - IRCWindow* m_window { nullptr }; -}; diff --git a/Userland/Applications/IRCClient/IRCChannelMemberListModel.cpp b/Userland/Applications/IRCClient/IRCChannelMemberListModel.cpp deleted file mode 100644 index 3dbd0997d0..0000000000 --- a/Userland/Applications/IRCClient/IRCChannelMemberListModel.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCChannelMemberListModel.h" -#include "IRCChannel.h" -#include -#include - -IRCChannelMemberListModel::IRCChannelMemberListModel(IRCChannel& channel) - : m_channel(channel) -{ -} - -IRCChannelMemberListModel::~IRCChannelMemberListModel() -{ -} - -int IRCChannelMemberListModel::row_count(const GUI::ModelIndex&) const -{ - return m_channel.member_count(); -} - -int IRCChannelMemberListModel::column_count(const GUI::ModelIndex&) const -{ - return 1; -} - -String IRCChannelMemberListModel::column_name(int column) const -{ - switch (column) { - case Column::Name: - return "Name"; - } - VERIFY_NOT_REACHED(); -} - -GUI::Variant IRCChannelMemberListModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::TextAlignment) - return Gfx::TextAlignment::CenterLeft; - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Name: - return m_channel.member_at(index.row()); - } - } - return {}; -} - -String IRCChannelMemberListModel::nick_at(const GUI::ModelIndex& index) const -{ - return data(index, GUI::ModelRole::Display).to_string(); -} diff --git a/Userland/Applications/IRCClient/IRCChannelMemberListModel.h b/Userland/Applications/IRCClient/IRCChannelMemberListModel.h deleted file mode 100644 index 12ca9ca739..0000000000 --- a/Userland/Applications/IRCClient/IRCChannelMemberListModel.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -class IRCChannel; - -class IRCChannelMemberListModel final : public GUI::Model { -public: - enum Column { - Name - }; - static NonnullRefPtr create(IRCChannel& channel) { return adopt_ref(*new IRCChannelMemberListModel(channel)); } - virtual ~IRCChannelMemberListModel() override; - - virtual int row_count(const GUI::ModelIndex&) const override; - virtual int column_count(const GUI::ModelIndex&) const override; - virtual String column_name(int column) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual String nick_at(const GUI::ModelIndex& index) const; - -private: - explicit IRCChannelMemberListModel(IRCChannel&); - - IRCChannel& m_channel; -}; diff --git a/Userland/Applications/IRCClient/IRCClient.cpp b/Userland/Applications/IRCClient/IRCClient.cpp deleted file mode 100644 index fc92f2f7bb..0000000000 --- a/Userland/Applications/IRCClient/IRCClient.cpp +++ /dev/null @@ -1,1164 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCClient.h" -#include "IRCAppWindow.h" -#include "IRCChannel.h" -#include "IRCLogBuffer.h" -#include "IRCQuery.h" -#include "IRCWindow.h" -#include "IRCWindowListModel.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -enum IRCNumeric { - RPL_WELCOME = 1, - RPL_WHOISUSER = 311, - RPL_WHOISSERVER = 312, - RPL_WHOISOPERATOR = 313, - RPL_ENDOFWHO = 315, - RPL_WHOISIDLE = 317, - RPL_ENDOFWHOIS = 318, - RPL_WHOISCHANNELS = 319, - RPL_TOPIC = 332, - RPL_TOPICWHOTIME = 333, - RPL_NAMREPLY = 353, - RPL_ENDOFNAMES = 366, - RPL_BANLIST = 367, - RPL_ENDOFBANLIST = 368, - RPL_ENDOFWHOWAS = 369, - RPL_ENDOFMOTD = 376, - ERR_NOSUCHNICK = 401, - ERR_UNKNOWNCOMMAND = 421, - ERR_NICKNAMEINUSE = 433, -}; - -IRCClient::IRCClient(String server, int port) - : m_nickname("seren1ty") - , m_client_window_list_model(IRCWindowListModel::create(*this)) - , m_log(IRCLogBuffer::create()) - , m_config(Core::ConfigFile::open_for_app("IRCClient", Core::ConfigFile::AllowWriting::Yes)) -{ - struct passwd* user_pw = getpwuid(getuid()); - m_socket = Core::TCPSocket::construct(this); - m_nickname = m_config->read_entry("User", "Nickname", String::formatted("{}_seren1ty", user_pw->pw_name)); - - if (server.is_empty()) { - m_hostname = m_config->read_entry("Connection", "Server", ""); - m_port = m_config->read_num_entry("Connection", "Port", 6667); - } else { - m_hostname = server; - m_port = port ? port : 6667; - } - - m_show_join_part_messages = m_config->read_bool_entry("Messaging", "ShowJoinPartMessages", 1); - m_show_nick_change_messages = m_config->read_bool_entry("Messaging", "ShowNickChangeMessages", 1); - - m_notify_on_message = m_config->read_bool_entry("Notifications", "NotifyOnMessage", 1); - m_notify_on_mention = m_config->read_bool_entry("Notifications", "NotifyOnMention", 1); - - m_ctcp_version_reply = m_config->read_entry("CTCP", "VersionReply", "IRC Client [x86] / Serenity OS"); - m_ctcp_userinfo_reply = m_config->read_entry("CTCP", "UserInfoReply", user_pw->pw_name); - m_ctcp_finger_reply = m_config->read_entry("CTCP", "FingerReply", user_pw->pw_name); -} - -IRCClient::~IRCClient() -{ -} - -void IRCClient::set_server(const String& hostname, int port) -{ - m_hostname = hostname; - m_port = port; - m_config->write_entry("Connection", "Server", hostname); - m_config->write_num_entry("Connection", "Port", port); - m_config->sync(); -} - -void IRCClient::on_socket_connected() -{ - m_notifier = Core::Notifier::construct(m_socket->fd(), Core::Notifier::Read); - m_notifier->on_ready_to_read = [this] { receive_from_server(); }; - - send_user(); - send_nick(); -} - -bool IRCClient::connect() -{ - if (m_socket->is_connected()) - VERIFY_NOT_REACHED(); - - m_socket->on_connected = [this] { on_socket_connected(); }; - - return m_socket->connect(m_hostname, m_port); -} - -void IRCClient::receive_from_server() -{ - while (m_socket->can_read_line()) { - auto line = m_socket->read_line(); - if (line.is_null()) { - if (!m_socket->is_connected()) { - outln("IRCClient: Connection closed!"); - exit(1); - } - VERIFY_NOT_REACHED(); - } - process_line(line); - } -} - -void IRCClient::process_line(const String& line) -{ - Message msg; - Vector prefix; - Vector command; - Vector current_parameter; - enum { - Start, - InPrefix, - InCommand, - InStartOfParameter, - InParameter, - InTrailingParameter, - } state - = Start; - - for (size_t i = 0; i < line.length(); ++i) { - char ch = line[i]; - if (ch == '\r') - continue; - if (ch == '\n') - break; - switch (state) { - case Start: - if (ch == ':') { - state = InPrefix; - continue; - } - state = InCommand; - [[fallthrough]]; - case InCommand: - if (ch == ' ') { - state = InStartOfParameter; - continue; - } - command.append(ch); - continue; - case InPrefix: - if (ch == ' ') { - state = InCommand; - continue; - } - prefix.append(ch); - continue; - case InStartOfParameter: - if (ch == ':') { - state = InTrailingParameter; - continue; - } - state = InParameter; - [[fallthrough]]; - case InParameter: - if (ch == ' ') { - if (!current_parameter.is_empty()) - msg.arguments.append(String(current_parameter.data(), current_parameter.size())); - current_parameter.clear_with_capacity(); - state = InStartOfParameter; - continue; - } - current_parameter.append(ch); - continue; - case InTrailingParameter: - current_parameter.append(ch); - continue; - } - } - if (!current_parameter.is_empty()) - msg.arguments.append(String::copy(current_parameter)); - msg.prefix = String::copy(prefix); - msg.command = String::copy(command); - handle(msg); -} - -void IRCClient::send(const String& text) -{ - if (!m_socket->send(text.bytes())) { - perror("send"); - exit(1); - } -} - -void IRCClient::send_user() -{ - send(String::formatted("USER {} 0 * :{}\r\n", m_nickname, m_nickname)); -} - -void IRCClient::send_nick() -{ - send(String::formatted("NICK {}\r\n", m_nickname)); -} - -void IRCClient::send_pong(const String& server) -{ - send(String::formatted("PONG {}\r\n", server)); - sleep(1); -} - -void IRCClient::join_channel(const String& channel_name) -{ - send(String::formatted("JOIN {}\r\n", channel_name)); -} - -void IRCClient::part_channel(const String& channel_name) -{ - send(String::formatted("PART {}\r\n", channel_name)); -} - -void IRCClient::send_whois(const String& nick) -{ - send(String::formatted("WHOIS {}\r\n", nick)); -} - -void IRCClient::handle(const Message& msg) -{ - if constexpr (IRC_DEBUG) { - outln("IRCClient::execute: prefix='{}', command='{}', arguments={}", - msg.prefix, - msg.command, - msg.arguments.size()); - - size_t index = 0; - for (auto& arg : msg.arguments) - outln(" [{}]: {}", index++, arg); - } - - auto numeric = msg.command.to_uint(); - - if (numeric.has_value()) { - switch (numeric.value()) { - case RPL_WELCOME: - return handle_rpl_welcome(msg); - case RPL_WHOISCHANNELS: - return handle_rpl_whoischannels(msg); - case RPL_ENDOFWHO: - return handle_rpl_endofwho(msg); - case RPL_ENDOFWHOIS: - return handle_rpl_endofwhois(msg); - case RPL_ENDOFWHOWAS: - return handle_rpl_endofwhowas(msg); - case RPL_ENDOFMOTD: - return handle_rpl_endofmotd(msg); - case RPL_WHOISOPERATOR: - return handle_rpl_whoisoperator(msg); - case RPL_WHOISSERVER: - return handle_rpl_whoisserver(msg); - case RPL_WHOISUSER: - return handle_rpl_whoisuser(msg); - case RPL_WHOISIDLE: - return handle_rpl_whoisidle(msg); - case RPL_TOPICWHOTIME: - return handle_rpl_topicwhotime(msg); - case RPL_TOPIC: - return handle_rpl_topic(msg); - case RPL_NAMREPLY: - return handle_rpl_namreply(msg); - case RPL_ENDOFNAMES: - return handle_rpl_endofnames(msg); - case RPL_BANLIST: - return handle_rpl_banlist(msg); - case RPL_ENDOFBANLIST: - return handle_rpl_endofbanlist(msg); - case ERR_NOSUCHNICK: - return handle_err_nosuchnick(msg); - case ERR_UNKNOWNCOMMAND: - return handle_err_unknowncommand(msg); - case ERR_NICKNAMEINUSE: - return handle_err_nicknameinuse(msg); - } - } - - if (msg.command == "PING") - return handle_ping(msg); - - if (msg.command == "JOIN") - return handle_join(msg); - - if (msg.command == "PART") - return handle_part(msg); - - if (msg.command == "QUIT") - return handle_quit(msg); - - if (msg.command == "TOPIC") - return handle_topic(msg); - - if (msg.command == "PRIVMSG") - return handle_privmsg_or_notice(msg, PrivmsgOrNotice::Privmsg); - - if (msg.command == "NOTICE") - return handle_privmsg_or_notice(msg, PrivmsgOrNotice::Notice); - - if (msg.command == "NICK") - return handle_nick(msg); - - if (msg.arguments.size() >= 2) - add_server_message(String::formatted("[{}] {}", msg.command, msg.arguments[1])); -} - -void IRCClient::add_server_message(const String& text, Color color) -{ - m_log->add_message(0, "", text, color); - m_server_subwindow->did_add_message(); -} - -void IRCClient::send_topic(const String& channel_name, const String& text) -{ - send(String::formatted("TOPIC {} :{}\r\n", channel_name, text)); -} - -void IRCClient::send_invite(const String& channel_name, const String& nick) -{ - send(String::formatted("INVITE {} {}\r\n", nick, channel_name)); -} - -void IRCClient::send_banlist(const String& channel_name) -{ - send(String::formatted("MODE {} +b\r\n", channel_name)); -} - -void IRCClient::send_voice_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} +v {}\r\n", channel_name, nick)); -} - -void IRCClient::send_devoice_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} -v {}\r\n", channel_name, nick)); -} - -void IRCClient::send_hop_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} +h {}\r\n", channel_name, nick)); -} - -void IRCClient::send_dehop_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} -h {}\r\n", channel_name, nick)); -} - -void IRCClient::send_op_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} +o {}\r\n", channel_name, nick)); -} - -void IRCClient::send_deop_user(const String& channel_name, const String& nick) -{ - send(String::formatted("MODE {} -o {}\r\n", channel_name, nick)); -} - -void IRCClient::send_kick(const String& channel_name, const String& nick, const String& comment) -{ - send(String::formatted("KICK {} {} :{}\r\n", channel_name, nick, comment)); -} - -void IRCClient::send_list() -{ - send("LIST\r\n"); -} - -void IRCClient::send_privmsg(const String& target, const String& text) -{ - send(String::formatted("PRIVMSG {} :{}\r\n", target, text)); -} - -void IRCClient::send_notice(const String& target, const String& text) -{ - send(String::formatted("NOTICE {} :{}\r\n", target, text)); -} - -void IRCClient::handle_user_input_in_channel(const String& channel_name, const String& input) -{ - if (input.is_empty()) - return; - if (input[0] == '/') - return handle_user_command(input); - ensure_channel(channel_name).say(input); -} - -void IRCClient::handle_user_input_in_query(const String& query_name, const String& input) -{ - if (input.is_empty()) - return; - if (input[0] == '/') - return handle_user_command(input); - ensure_query(query_name).say(input); -} - -void IRCClient::handle_user_input_in_server(const String& input) -{ - if (input.is_empty()) - return; - if (input[0] == '/') - return handle_user_command(input); -} - -String IRCClient::nick_without_prefix(const String& nick) -{ - assert(!nick.is_empty()); - if (IRCClient::is_nick_prefix(nick[0])) - return nick.substring(1, nick.length() - 1); - return nick; -} - -bool IRCClient::is_nick_prefix(char ch) -{ - switch (ch) { - case '@': - case '+': - case '~': - case '&': - case '%': - return true; - } - return false; -} - -bool IRCClient::is_channel_prefix(char ch) -{ - switch (ch) { - case '&': - case '#': - case '+': - case '!': - return true; - } - return false; -} - -static bool has_ctcp_payload(const StringView& string) -{ - return string.length() >= 2 && string[0] == 0x01 && string[string.length() - 1] == 0x01; -} - -void IRCClient::handle_privmsg_or_notice(const Message& msg, PrivmsgOrNotice type) -{ - if (msg.arguments.size() < 2) - return; - if (msg.prefix.is_empty()) - return; - auto parts = msg.prefix.split('!'); - auto sender_nick = parts[0]; - auto target = msg.arguments[0]; - - bool is_ctcp = has_ctcp_payload(msg.arguments[1]); - - outln_if(IRC_DEBUG, "handle_privmsg_or_notice: type='{}'{}, sender_nick='{}', target='{}'", - type == PrivmsgOrNotice::Privmsg ? "privmsg" : "notice", - is_ctcp ? " (ctcp)" : "", - sender_nick, - target); - - if (sender_nick.is_empty()) - return; - - char sender_prefix = 0; - if (is_nick_prefix(sender_nick[0])) { - sender_prefix = sender_nick[0]; - sender_nick = sender_nick.substring(1, sender_nick.length() - 1); - } - - String message_text = msg.arguments[1]; - auto message_color = Color::Black; - - bool insert_as_raw_message = false; - - if (is_ctcp) { - auto ctcp_payload = msg.arguments[1].substring_view(1, msg.arguments[1].length() - 2); - if (type == PrivmsgOrNotice::Privmsg) - handle_ctcp_request(sender_nick, ctcp_payload); - else - handle_ctcp_response(sender_nick, ctcp_payload); - - if (ctcp_payload.starts_with("ACTION")) { - insert_as_raw_message = true; - message_text = String::formatted("* {}{}", sender_nick, ctcp_payload.substring_view(6, ctcp_payload.length() - 6)); - message_color = Color::Magenta; - } else { - message_text = String::formatted("(CTCP) {}", ctcp_payload); - message_color = Color::Blue; - } - } - - { - auto it = m_channels.find(target); - if (it != m_channels.end()) { - if (insert_as_raw_message) - (*it).value->add_message(message_text, message_color); - else - (*it).value->add_message(sender_prefix, sender_nick, message_text, message_color); - return; - } - } - - // For NOTICE or CTCP messages, only put them in query if one already exists. - // Otherwise, put them in the server window. This seems to match other clients. - IRCQuery* query = nullptr; - if (is_ctcp || type == PrivmsgOrNotice::Notice) { - query = query_with_name(target); - } else { - query = &ensure_query(target); - } - if (query) { - if (insert_as_raw_message) - query->add_message(message_text, message_color); - else - query->add_message(sender_prefix, sender_nick, message_text, message_color); - } else { - add_server_message(String::formatted("<{}> {}", sender_nick, message_text), message_color); - } -} - -IRCQuery* IRCClient::query_with_name(const String& name) -{ - return const_cast(m_queries.get(name).value_or(nullptr)); -} - -IRCQuery& IRCClient::ensure_query(const String& name) -{ - auto it = m_queries.find(name); - if (it != m_queries.end()) - return *(*it).value; - auto query = IRCQuery::create(*this, name); - auto& query_reference = *query; - m_queries.set(name, query); - return query_reference; -} - -IRCChannel& IRCClient::ensure_channel(const String& name) -{ - auto it = m_channels.find(name); - if (it != m_channels.end()) - return *(*it).value; - auto channel = IRCChannel::create(*this, name); - auto& channel_reference = *channel; - m_channels.set(name, channel); - return channel_reference; -} - -void IRCClient::handle_ping(const Message& msg) -{ - if (msg.arguments.size() < 1) - return; - m_log->add_message(0, "", "Ping? Pong!"); - send_pong(msg.arguments[0]); -} - -void IRCClient::handle_join(const Message& msg) -{ - if (msg.arguments.size() != 1) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& channel_name = msg.arguments[0]; - ensure_channel(channel_name).handle_join(nick, msg.prefix); -} - -void IRCClient::handle_part(const Message& msg) -{ - if (msg.arguments.size() < 1) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& channel_name = msg.arguments[0]; - ensure_channel(channel_name).handle_part(nick, msg.prefix); -} - -void IRCClient::handle_quit(const Message& msg) -{ - if (msg.arguments.size() < 1) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& message = msg.arguments[0]; - for (auto& it : m_channels) { - it.value->handle_quit(nick, msg.prefix, message); - } -} - -void IRCClient::handle_nick(const Message& msg) -{ - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto old_nick = prefix_parts[0]; - if (msg.arguments.size() != 1) - return; - auto& new_nick = msg.arguments[0]; - if (old_nick == m_nickname) - m_nickname = new_nick; - if (m_show_nick_change_messages) - add_server_message(String::formatted("~ {} changed nickname to {}", old_nick, new_nick)); - if (on_nickname_changed) - on_nickname_changed(new_nick); - for (auto& it : m_channels) { - it.value->notify_nick_changed(old_nick, new_nick); - } -} - -void IRCClient::handle_topic(const Message& msg) -{ - if (msg.arguments.size() != 2) - return; - auto prefix_parts = msg.prefix.split('!'); - if (prefix_parts.size() < 1) - return; - auto nick = prefix_parts[0]; - auto& channel_name = msg.arguments[0]; - ensure_channel(channel_name).handle_topic(nick, msg.arguments[1]); -} - -void IRCClient::handle_rpl_welcome(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& welcome_message = msg.arguments[1]; - add_server_message(welcome_message); - - auto channel_str = m_config->read_entry("Connection", "AutoJoinChannels", ""); - if (channel_str.is_empty()) - return; - dbgln("IRCClient: Channels to autojoin: {}", channel_str); - auto channels = channel_str.split(','); - for (auto& channel : channels) { - join_channel(channel); - dbgln("IRCClient: Auto joining channel: {}", channel); - } -} - -void IRCClient::handle_rpl_topic(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& channel_name = msg.arguments[1]; - auto& topic = msg.arguments[2]; - ensure_channel(channel_name).handle_topic({}, topic); -} - -void IRCClient::handle_rpl_namreply(const Message& msg) -{ - if (msg.arguments.size() < 4) - return; - auto& channel_name = msg.arguments[2]; - auto& channel = ensure_channel(channel_name); - auto members = msg.arguments[3].split(' '); - - quick_sort(members, [](auto& a, auto& b) { - return strcasecmp(a.characters(), b.characters()) < 0; - }); - - for (auto& member : members) { - if (member.is_empty()) - continue; - char prefix = 0; - if (is_nick_prefix(member[0])) - prefix = member[0]; - channel.add_member(member, prefix); - } -} - -void IRCClient::handle_rpl_endofnames(const Message&) -{ - add_server_message("// End of NAMES"); -} - -void IRCClient::handle_rpl_banlist(const Message& msg) -{ - if (msg.arguments.size() < 5) - return; - auto& channel = msg.arguments[1]; - auto& mask = msg.arguments[2]; - auto& user = msg.arguments[3]; - auto& datestamp = msg.arguments[4]; - add_server_message(String::formatted("* {}: {} on {} by {}", channel, mask, datestamp, user)); -} - -void IRCClient::handle_rpl_endofbanlist(const Message&) -{ - add_server_message("// End of BANLIST"); -} - -void IRCClient::handle_rpl_endofwho(const Message&) -{ - add_server_message("// End of WHO"); -} - -void IRCClient::handle_rpl_endofwhois(const Message&) -{ - add_server_message("// End of WHOIS"); -} - -void IRCClient::handle_rpl_endofwhowas(const Message&) -{ - add_server_message("// End of WHOWAS"); -} - -void IRCClient::handle_rpl_endofmotd(const Message&) -{ - add_server_message("// End of MOTD"); -} - -void IRCClient::handle_rpl_whoisoperator(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& nick = msg.arguments[1]; - add_server_message(String::formatted("* {} is an IRC operator", nick)); -} - -void IRCClient::handle_rpl_whoisserver(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& server = msg.arguments[2]; - add_server_message(String::formatted("* {} is using server {}", nick, server)); -} - -void IRCClient::handle_rpl_whoisuser(const Message& msg) -{ - if (msg.arguments.size() < 6) - return; - auto& nick = msg.arguments[1]; - auto& username = msg.arguments[2]; - auto& host = msg.arguments[3]; - [[maybe_unused]] auto& asterisk = msg.arguments[4]; - auto& realname = msg.arguments[5]; - add_server_message(String::formatted("* {} is {}@{}, real name: {}", nick, username, host, realname)); -} - -void IRCClient::handle_rpl_whoisidle(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& secs = msg.arguments[2]; - add_server_message(String::formatted("* {} is {} seconds idle", nick, secs)); -} - -void IRCClient::handle_rpl_whoischannels(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& channel_list = msg.arguments[2]; - add_server_message(String::formatted("* {} is in channels {}", nick, channel_list)); -} - -void IRCClient::handle_rpl_topicwhotime(const Message& msg) -{ - if (msg.arguments.size() < 4) - return; - auto& channel_name = msg.arguments[1]; - auto& nick = msg.arguments[2]; - auto setat = msg.arguments[3]; - auto setat_time = setat.to_uint(); - if (setat_time.has_value()) - setat = Core::DateTime::from_timestamp(setat_time.value()).to_string(); - ensure_channel(channel_name).add_message(String::formatted("*** (set by {} at {})", nick, setat), Color::Blue); -} - -void IRCClient::handle_err_nosuchnick(const Message& msg) -{ - if (msg.arguments.size() < 3) - return; - auto& nick = msg.arguments[1]; - auto& message = msg.arguments[2]; - add_server_message(String::formatted("* {} :{}", nick, message)); -} - -void IRCClient::handle_err_unknowncommand(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& cmd = msg.arguments[1]; - add_server_message(String::formatted("* Unknown command: {}", cmd)); -} - -void IRCClient::handle_err_nicknameinuse(const Message& msg) -{ - if (msg.arguments.size() < 2) - return; - auto& nick = msg.arguments[1]; - add_server_message(String::formatted("* {} :Nickname in use", nick)); -} - -void IRCClient::register_subwindow(IRCWindow& subwindow) -{ - if (subwindow.type() == IRCWindow::Server) { - m_server_subwindow = &subwindow; - subwindow.set_log_buffer(*m_log); - } - m_windows.append(&subwindow); - m_client_window_list_model->invalidate(); -} - -void IRCClient::unregister_subwindow(IRCWindow& subwindow) -{ - if (subwindow.type() == IRCWindow::Server) { - m_server_subwindow = &subwindow; - } - for (size_t i = 0; i < m_windows.size(); ++i) { - if (m_windows.at(i) == &subwindow) { - m_windows.remove(i); - break; - } - } - m_client_window_list_model->invalidate(); -} - -void IRCClient::handle_user_command(const String& input) -{ - auto parts = input.split_view(' '); - if (parts.is_empty()) - return; - auto command = String(parts[0]).to_uppercase(); - if (command == "/RAW") { - if (parts.size() <= 1) - return; - int command_length = command.length() + 1; - StringView raw_message = input.view().substring_view(command_length, input.view().length() - command_length); - send(String::formatted("{}\r\n", raw_message)); - return; - } - if (command == "/NICK") { - if (parts.size() >= 2) - change_nick(parts[1]); - return; - } - if (command == "/JOIN") { - if (parts.size() >= 2) - join_channel(parts[1]); - return; - } - if (command == "/PART") { - if (parts.size() >= 2) { - auto channel = parts[1]; - part_channel(channel); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - join_channel(channel); - } - return; - } - if (command == "/CYCLE") { - if (parts.size() >= 2) { - auto channel = parts[1]; - part_channel(channel); - join_channel(channel); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - part_channel(channel); - join_channel(channel); - } - return; - } - if (command == "/BANLIST") { - if (parts.size() >= 2) { - auto channel = parts[1]; - send_banlist(channel); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - send_banlist(channel); - } - return; - } - if (command == "/ME") { - if (parts.size() < 2) - return; - - auto* window = current_window(); - if (!window) - return; - - auto emote = input.view().substring_view_starting_after_substring(parts[0]); - auto action_string = String::formatted("ACTION{}", emote); - String peer; - if (window->type() == IRCWindow::Type::Channel) { - peer = window->channel().name(); - window->channel().add_message(String::formatted("* {}{}", m_nickname, emote), Gfx::Color::Magenta); - } else if (window->type() == IRCWindow::Type::Query) { - peer = window->query().name(); - window->query().add_message(String::formatted("* {}{}", m_nickname, emote), Gfx::Color::Magenta); - } else { - return; - } - - send_ctcp_request(peer, action_string); - return; - } - if (command == "/TOPIC") { - if (parts.size() < 2) - return; - if (parts[1].is_empty()) - return; - - if (is_channel_prefix(parts[1][0])) { - if (parts.size() < 3) - return; - auto channel = parts[1]; - auto topic = input.view().substring_view_starting_after_substring(channel); - send_topic(channel, topic); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - auto topic = input.view().substring_view_starting_after_substring(parts[0]); - send_topic(channel, topic); - } - return; - } - if (command == "/KICK") { - if (parts.size() < 2) - return; - if (parts[1].is_empty()) - return; - - if (is_channel_prefix(parts[1][0])) { - if (parts.size() < 3) - return; - auto channel = parts[1]; - auto nick = parts[2]; - auto reason = input.view().substring_view_starting_after_substring(nick); - send_kick(channel, nick, reason); - } else { - auto* window = current_window(); - if (!window || window->type() != IRCWindow::Type::Channel) - return; - auto channel = window->channel().name(); - auto nick = parts[1]; - auto reason = input.view().substring_view_starting_after_substring(nick); - send_kick(channel, nick, reason); - } - return; - } - if (command == "/LIST") { - send_list(); - return; - } - if (command == "/QUERY") { - if (parts.size() >= 2) { - auto& query = ensure_query(parts[1]); - IRCAppWindow::the().set_active_window(query.window()); - } - return; - } - if (command == "/MSG") { - if (parts.size() < 3) - return; - auto nick = parts[1]; - auto& query = ensure_query(nick); - IRCAppWindow::the().set_active_window(query.window()); - query.say(input.view().substring_view_starting_after_substring(nick)); - return; - } - if (command == "/WHOIS") { - if (parts.size() >= 2) - send_whois(parts[1]); - return; - } -} - -void IRCClient::change_nick(const String& nick) -{ - send(String::formatted("NICK {}\r\n", nick)); -} - -void IRCClient::handle_list_channels_action() -{ - send_list(); -} - -void IRCClient::handle_whois_action(const String& nick) -{ - send_whois(nick); -} - -void IRCClient::handle_ctcp_user_action(const String& nick, const String& message) -{ - send_ctcp_request(nick, message); -} - -void IRCClient::handle_open_query_action(const String& nick) -{ - ensure_query(nick); -} - -void IRCClient::handle_change_nick_action(const String& nick) -{ - change_nick(nick); -} - -void IRCClient::handle_change_topic_action(const String& channel, const String& topic) -{ - send_topic(channel, topic); -} - -void IRCClient::handle_invite_user_action(const String& channel, const String& nick) -{ - send_invite(channel, nick); -} - -void IRCClient::handle_banlist_action(const String& channel) -{ - send_banlist(channel); -} - -void IRCClient::handle_voice_user_action(const String& channel, const String& nick) -{ - send_voice_user(channel, nick); -} - -void IRCClient::handle_devoice_user_action(const String& channel, const String& nick) -{ - send_devoice_user(channel, nick); -} - -void IRCClient::handle_hop_user_action(const String& channel, const String& nick) -{ - send_hop_user(channel, nick); -} - -void IRCClient::handle_dehop_user_action(const String& channel, const String& nick) -{ - send_dehop_user(channel, nick); -} - -void IRCClient::handle_op_user_action(const String& channel, const String& nick) -{ - send_op_user(channel, nick); -} - -void IRCClient::handle_deop_user_action(const String& channel, const String& nick) -{ - send_deop_user(channel, nick); -} - -void IRCClient::handle_kick_user_action(const String& channel, const String& nick, const String& message) -{ - send_kick(channel, nick, message); -} - -void IRCClient::handle_close_query_action(const String& nick) -{ - m_queries.remove(nick); - m_client_window_list_model->invalidate(); -} - -void IRCClient::handle_join_action(const String& channel) -{ - join_channel(channel); -} - -void IRCClient::handle_part_action(const String& channel) -{ - part_channel(channel); -} - -void IRCClient::handle_cycle_channel_action(const String& channel) -{ - part_channel(channel); - join_channel(channel); -} - -void IRCClient::did_part_from_channel(Badge, IRCChannel& channel) -{ - if (on_part_from_channel) - on_part_from_channel(channel); -} - -void IRCClient::send_ctcp_response(const StringView& peer, const StringView& payload) -{ - StringBuilder builder; - builder.append(0x01); - builder.append(payload); - builder.append(0x01); - auto message = builder.to_string(); - send_notice(peer, message); -} - -void IRCClient::send_ctcp_request(const StringView& peer, const StringView& payload) -{ - StringBuilder builder; - builder.append(0x01); - builder.append(payload); - builder.append(0x01); - auto message = builder.to_string(); - send_privmsg(peer, message); -} - -void IRCClient::handle_ctcp_request(const StringView& peer, const StringView& payload) -{ - dbgln("handle_ctcp_request: {}", payload); - - if (payload == "VERSION") { - auto version = ctcp_version_reply(); - if (version.is_empty()) - return; - send_ctcp_response(peer, String::formatted("VERSION {}", version)); - return; - } - - if (payload == "USERINFO") { - auto userinfo = ctcp_userinfo_reply(); - if (userinfo.is_empty()) - return; - send_ctcp_response(peer, String::formatted("USERINFO {}", userinfo)); - return; - } - - if (payload == "FINGER") { - auto finger = ctcp_finger_reply(); - if (finger.is_empty()) - return; - send_ctcp_response(peer, String::formatted("FINGER {}", finger)); - return; - } - - if (payload.starts_with("PING")) { - send_ctcp_response(peer, payload); - return; - } -} - -void IRCClient::handle_ctcp_response(const StringView& peer, const StringView& payload) -{ - dbgln("handle_ctcp_response({}): {}", peer, payload); -} diff --git a/Userland/Applications/IRCClient/IRCClient.h b/Userland/Applications/IRCClient/IRCClient.h deleted file mode 100644 index 15f3e7c223..0000000000 --- a/Userland/Applications/IRCClient/IRCClient.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "IRCLogBuffer.h" -#include "IRCWindow.h" -#include -#include -#include -#include -#include -#include - -class IRCChannel; -class IRCQuery; -class IRCWindowListModel; - -class IRCClient final : public Core::Object { - C_OBJECT(IRCClient) - friend class IRCChannel; - friend class IRCQuery; - -public: - virtual ~IRCClient() override; - - void set_server(const String& hostname, int port = 6667); - - bool connect(); - - String hostname() const { return m_hostname; } - int port() const { return m_port; } - - String nickname() const { return m_nickname; } - - String ctcp_version_reply() const { return m_ctcp_version_reply; } - String ctcp_userinfo_reply() const { return m_ctcp_userinfo_reply; } - String ctcp_finger_reply() const { return m_ctcp_finger_reply; } - - bool show_join_part_messages() const { return m_show_join_part_messages; } - bool show_nick_change_messages() const { return m_show_nick_change_messages; } - - bool notify_on_message() const { return m_notify_on_message; } - bool notify_on_mention() const { return m_notify_on_mention; } - - void join_channel(const String&); - void part_channel(const String&); - void change_nick(const String&); - - static bool is_nick_prefix(char); - static bool is_channel_prefix(char); - String nick_without_prefix(const String& nick); - - IRCWindow* current_window() { return aid_get_active_window(); } - const IRCWindow* current_window() const { return aid_get_active_window(); } - - Function on_disconnect; - Function on_server_message; - Function on_nickname_changed; - Function on_part_from_channel; - - Function(void*, IRCWindow::Type, const String&)> aid_create_window; - Function aid_get_active_window; - Function aid_update_window_list; - - void register_subwindow(IRCWindow&); - void unregister_subwindow(IRCWindow&); - - IRCWindowListModel* client_window_list_model() { return m_client_window_list_model.ptr(); } - const IRCWindowListModel* client_window_list_model() const { return m_client_window_list_model.ptr(); } - - int window_count() const { return m_windows.size(); } - const IRCWindow& window_at(int index) const { return *m_windows.at(index); } - IRCWindow& window_at(int index) { return *m_windows.at(index); } - - size_t window_index(const IRCWindow& window) const - { - for (size_t i = 0; i < m_windows.size(); ++i) { - if (m_windows[i] == &window) - return i; - } - VERIFY_NOT_REACHED(); - } - - void did_part_from_channel(Badge, IRCChannel&); - - void handle_user_input_in_channel(const String& channel_name, const String&); - void handle_user_input_in_query(const String& query_name, const String&); - void handle_user_input_in_server(const String&); - - void handle_list_channels_action(); - void handle_whois_action(const String& nick); - void handle_ctcp_user_action(const String& nick, const String& message); - void handle_open_query_action(const String&); - void handle_close_query_action(const String&); - void handle_join_action(const String& channel_name); - void handle_part_action(const String& channel_name); - void handle_cycle_channel_action(const String& channel_name); - void handle_change_nick_action(const String& nick); - void handle_change_topic_action(const String& channel_name, const String&); - void handle_invite_user_action(const String& channel_name, const String& nick); - void handle_banlist_action(const String& channel_name); - void handle_voice_user_action(const String& channel_name, const String& nick); - void handle_devoice_user_action(const String& channel_name, const String& nick); - void handle_hop_user_action(const String& channel_name, const String& nick); - void handle_dehop_user_action(const String& channel_name, const String& nick); - void handle_op_user_action(const String& channel_name, const String& nick); - void handle_deop_user_action(const String& channel_name, const String& nick); - void handle_kick_user_action(const String& channel_name, const String& nick, const String&); - - IRCQuery* query_with_name(const String&); - IRCQuery& ensure_query(const String& name); - IRCChannel& ensure_channel(const String& name); - - void add_server_message(const String&, Color = Color::Black); - -private: - IRCClient(String server, int port); - - struct Message { - String prefix; - String command; - Vector arguments; - }; - - enum class PrivmsgOrNotice { - Privmsg, - Notice, - }; - - void receive_from_server(); - void send(const String&); - void send_user(); - void send_nick(); - void send_pong(const String& server); - void send_privmsg(const String& target, const String&); - void send_notice(const String& target, const String&); - void send_topic(const String& channel_name, const String&); - void send_invite(const String& channel_name, const String& nick); - void send_banlist(const String& channel_name); - void send_voice_user(const String& channel_name, const String& nick); - void send_devoice_user(const String& channel_name, const String& nick); - void send_hop_user(const String& channel_name, const String& nick); - void send_dehop_user(const String& channel_name, const String& nick); - void send_op_user(const String& channel_name, const String& nick); - void send_deop_user(const String& channel_name, const String& nick); - void send_kick(const String& channel_name, const String& nick, const String&); - void send_list(); - void send_whois(const String&); - void process_line(const String&); - void handle_join(const Message&); - void handle_part(const Message&); - void handle_quit(const Message&); - void handle_ping(const Message&); - void handle_topic(const Message&); - void handle_rpl_welcome(const Message&); - void handle_rpl_topic(const Message&); - void handle_rpl_whoisuser(const Message&); - void handle_rpl_whoisserver(const Message&); - void handle_rpl_whoisoperator(const Message&); - void handle_rpl_whoisidle(const Message&); - void handle_rpl_endofwho(const Message&); - void handle_rpl_endofwhois(const Message&); - void handle_rpl_endofwhowas(const Message&); - void handle_rpl_endofmotd(const Message&); - void handle_rpl_whoischannels(const Message&); - void handle_rpl_topicwhotime(const Message&); - void handle_rpl_endofnames(const Message&); - void handle_rpl_endofbanlist(const Message&); - void handle_rpl_namreply(const Message&); - void handle_rpl_banlist(const Message&); - void handle_err_nosuchnick(const Message&); - void handle_err_unknowncommand(const Message&); - void handle_err_nicknameinuse(const Message&); - void handle_privmsg_or_notice(const Message&, PrivmsgOrNotice); - void handle_nick(const Message&); - void handle(const Message&); - void handle_user_command(const String&); - void handle_ctcp_request(const StringView& peer, const StringView& payload); - void handle_ctcp_response(const StringView& peer, const StringView& payload); - void send_ctcp_request(const StringView& peer, const StringView& payload); - void send_ctcp_response(const StringView& peer, const StringView& payload); - - void on_socket_connected(); - - String m_hostname; - int m_port { 6667 }; - - RefPtr m_socket; - - String m_nickname; - RefPtr m_notifier; - HashMap, CaseInsensitiveStringTraits> m_channels; - HashMap, CaseInsensitiveStringTraits> m_queries; - - bool m_show_join_part_messages { 1 }; - bool m_show_nick_change_messages { 1 }; - - bool m_notify_on_message { 1 }; - bool m_notify_on_mention { 1 }; - - String m_ctcp_version_reply; - String m_ctcp_userinfo_reply; - String m_ctcp_finger_reply; - - Vector m_windows; - - IRCWindow* m_server_subwindow { nullptr }; - - NonnullRefPtr m_client_window_list_model; - NonnullRefPtr m_log; - NonnullRefPtr m_config; -}; diff --git a/Userland/Applications/IRCClient/IRCLogBuffer.cpp b/Userland/Applications/IRCClient/IRCLogBuffer.cpp deleted file mode 100644 index 9ac766d05b..0000000000 --- a/Userland/Applications/IRCClient/IRCLogBuffer.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCLogBuffer.h" -#include -#include -#include -#include -#include - -NonnullRefPtr IRCLogBuffer::create() -{ - return adopt_ref(*new IRCLogBuffer); -} - -IRCLogBuffer::IRCLogBuffer() -{ - m_document = Web::DOM::Document::create(); - m_document->append_child(adopt_ref(*new Web::DOM::DocumentType(document()))); - auto html_element = m_document->create_element("html"); - m_document->append_child(html_element); - auto head_element = m_document->create_element("head"); - html_element->append_child(head_element); - auto style_element = m_document->create_element("style"); - style_element->append_child(adopt_ref(*new Web::DOM::Text(document(), "div { font-family: Csilla; font-weight: lighter; }"))); - head_element->append_child(style_element); - auto body_element = m_document->create_element("body"); - html_element->append_child(body_element); - m_container_element = body_element; -} - -IRCLogBuffer::~IRCLogBuffer() -{ -} - -static String timestamp_string() -{ - auto now = time(nullptr); - auto* tm = localtime(&now); - return String::formatted("{:02}:{:02}:{:02} ", tm->tm_hour, tm->tm_min, tm->tm_sec); -} - -void IRCLogBuffer::add_message(char prefix, const String& name, const String& text, Color color) -{ - auto nick_string = String::formatted("<{}{}> ", prefix ? prefix : ' ', name.characters()); - auto html = String::formatted( - "{}" - "{}" - "{}", - timestamp_string(), - escape_html_entities(nick_string), - escape_html_entities(text)); - - auto wrapper = m_document->create_element(Web::HTML::TagNames::div); - wrapper->set_attribute(Web::HTML::AttributeNames::style, String::formatted("color: {}", color.to_string())); - wrapper->set_inner_html(html); - m_container_element->append_child(wrapper); - m_document->force_layout(); -} - -void IRCLogBuffer::add_message(const String& text, Color color) -{ - auto html = String::formatted( - "{}" - "{}", - timestamp_string(), - escape_html_entities(text)); - auto wrapper = m_document->create_element(Web::HTML::TagNames::div); - wrapper->set_attribute(Web::HTML::AttributeNames::style, String::formatted("color: {}", color.to_string())); - wrapper->set_inner_html(html); - m_container_element->append_child(wrapper); - m_document->force_layout(); -} diff --git a/Userland/Applications/IRCClient/IRCLogBuffer.h b/Userland/Applications/IRCClient/IRCLogBuffer.h deleted file mode 100644 index 5afe118da1..0000000000 --- a/Userland/Applications/IRCClient/IRCLogBuffer.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -class IRCLogBuffer : public RefCounted { -public: - static NonnullRefPtr create(); - ~IRCLogBuffer(); - - struct Message { - time_t timestamp { 0 }; - char prefix { 0 }; - String sender; - String text; - Color color { Color::Black }; - }; - - void add_message(char prefix, const String& name, const String& text, Color = Color::Black); - void add_message(const String& text, Color = Color::Black); - - const Web::DOM::Document& document() const { return *m_document; } - Web::DOM::Document& document() { return *m_document; } - -private: - IRCLogBuffer(); - RefPtr m_document; - RefPtr m_container_element; -}; diff --git a/Userland/Applications/IRCClient/IRCQuery.cpp b/Userland/Applications/IRCClient/IRCQuery.cpp deleted file mode 100644 index 553ad36d1e..0000000000 --- a/Userland/Applications/IRCClient/IRCQuery.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCQuery.h" -#include "IRCClient.h" - -IRCQuery::IRCQuery(IRCClient& client, const String& name) - : m_client(client) - , m_name(name) - , m_log(IRCLogBuffer::create()) -{ - m_window = m_client->aid_create_window(this, IRCWindow::Query, m_name); - m_window->set_log_buffer(*m_log); -} - -IRCQuery::~IRCQuery() -{ -} - -NonnullRefPtr IRCQuery::create(IRCClient& client, const String& name) -{ - return adopt_ref(*new IRCQuery(client, name)); -} - -void IRCQuery::add_message(char prefix, const String& name, const String& text, Color color) -{ - log().add_message(prefix, name, text, color); - window().did_add_message(name, text); -} - -void IRCQuery::add_message(const String& text, Color color) -{ - log().add_message(text, color); - window().did_add_message(); -} - -void IRCQuery::say(const String& text) -{ - m_client->send_privmsg(m_name, text); - add_message(' ', m_client->nickname(), text); -} diff --git a/Userland/Applications/IRCClient/IRCQuery.h b/Userland/Applications/IRCClient/IRCQuery.h deleted file mode 100644 index dd4d054478..0000000000 --- a/Userland/Applications/IRCClient/IRCQuery.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "IRCLogBuffer.h" -#include -#include -#include -#include -#include - -class IRCClient; -class IRCWindow; - -class IRCQuery : public RefCounted { -public: - static NonnullRefPtr create(IRCClient&, const String& name); - ~IRCQuery(); - - String name() const { return m_name; } - void add_message(char prefix, const String& name, const String& text, Color = Color::Black); - void add_message(const String& text, Color = Color::Black); - - const IRCLogBuffer& log() const { return *m_log; } - IRCLogBuffer& log() { return *m_log; } - - void say(const String&); - - IRCWindow& window() { return *m_window; } - const IRCWindow& window() const { return *m_window; } - -private: - IRCQuery(IRCClient&, const String& name); - - NonnullRefPtr m_client; - String m_name; - RefPtr m_window; - - NonnullRefPtr m_log; -}; diff --git a/Userland/Applications/IRCClient/IRCWindow.cpp b/Userland/Applications/IRCClient/IRCWindow.cpp deleted file mode 100644 index de822d6f8d..0000000000 --- a/Userland/Applications/IRCClient/IRCWindow.cpp +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCWindow.h" -#include "IRCChannel.h" -#include "IRCChannelMemberListModel.h" -#include "IRCClient.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -IRCWindow::IRCWindow(IRCClient& client, void* owner, Type type, const String& name) - : m_client(client) - , m_owner(owner) - , m_type(type) - , m_name(name) -{ - set_layout(); - - // Make a container for the log buffer view + (optional) member list. - auto& container = add(); - - m_page_view = container.add(); - - if (m_type == Channel) { - auto& member_view = container.add(); - member_view.set_column_headers_visible(false); - member_view.set_fixed_width(100); - member_view.set_alternating_row_colors(false); - member_view.set_model(channel().member_model()); - member_view.set_activates_on_selection(true); - member_view.on_activation = [&](auto& index) { - if (!index.is_valid()) - return; - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_open_query_action(m_client->nick_without_prefix(nick.characters())); - }; - member_view.on_context_menu_request = [&](const GUI::ModelIndex& index, const GUI::ContextMenuEvent& event) { - if (!index.is_valid()) - return; - - m_context_menu = GUI::Menu::construct(); - - m_context_menu->add_action(GUI::Action::create("Open &Query", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-open-query.png"), [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_open_query_action(m_client->nick_without_prefix(nick.characters())); - })); - - m_context_menu->add_action(GUI::Action::create("Whois", Gfx::Bitmap::try_load_from_file("/res/icons/16x16/irc-whois.png"), [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_whois_action(m_client->nick_without_prefix(nick.characters())); - })); - - auto& context_control_menu = m_context_menu->add_submenu("Control"); - - context_control_menu.add_action(GUI::Action::create("&Voice", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_voice_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("DeVoice", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_devoice_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("Hop", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_hop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("DeHop", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_dehop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("&Op", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_op_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_action(GUI::Action::create("DeOp", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_deop_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters())); - })); - - context_control_menu.add_separator(); - - context_control_menu.add_action(GUI::Action::create("&Kick", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - if (IRCClient::is_nick_prefix(nick[0])) - nick = nick.substring(1, nick.length() - 1); - String value; - if (GUI::InputBox::show(window(), value, "Enter reason:", "Reason") == GUI::InputBox::ExecOK) - m_client->handle_kick_user_action(m_name.characters(), m_client->nick_without_prefix(nick.characters()), value); - })); - - auto& context_ctcp_menu = m_context_menu->add_submenu("CTCP"); - - context_ctcp_menu.add_action(GUI::Action::create("&User Info", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "USERINFO"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("&Finger", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "FINGER"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("&Time", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "TIME"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("&Version", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "VERSION"); - })); - - context_ctcp_menu.add_action(GUI::Action::create("&Client Info", [&](const GUI::Action&) { - auto nick = channel().member_model()->nick_at(member_view.selection().first()); - if (nick.is_empty()) - return; - m_client->handle_ctcp_user_action(m_client->nick_without_prefix(nick.characters()), "CLIENTINFO"); - })); - - m_context_menu->popup(event.screen_position()); - }; - } - - m_text_box = add(); - m_text_box->set_fixed_height(19); - m_text_box->on_return_pressed = [this] { - if (m_type == Channel) - m_client->handle_user_input_in_channel(m_name, m_text_box->text()); - else if (m_type == Query) - m_client->handle_user_input_in_query(m_name, m_text_box->text()); - else if (m_type == Server) - m_client->handle_user_input_in_server(m_text_box->text()); - m_text_box->add_current_text_to_history(); - m_text_box->clear(); - }; - m_text_box->set_history_enabled(true); - m_text_box->set_placeholder("Message"); - - m_client->register_subwindow(*this); -} - -IRCWindow::~IRCWindow() -{ - m_client->unregister_subwindow(*this); -} - -void IRCWindow::set_log_buffer(const IRCLogBuffer& log_buffer) -{ - m_log_buffer = &log_buffer; - m_page_view->set_document(const_cast(&log_buffer.document())); -} - -bool IRCWindow::is_active() const -{ - return m_client->current_window() == this; -} - -void IRCWindow::post_notification_if_needed(const String& name, const String& message) -{ - if (name.is_null() || message.is_null()) - return; - if (is_active() && window()->is_active()) - return; - - auto notification = GUI::Notification::construct(); - - if (type() == Type::Channel) { - if (!m_client->notify_on_mention()) - return; - if (!message.contains(m_client->nickname())) - return; - - StringBuilder builder; - builder.append(name); - builder.append(" in "); - builder.append(m_name); - notification->set_title(builder.to_string()); - } else { - if (!m_client->notify_on_message()) - return; - notification->set_title(name); - } - - notification->set_icon(Gfx::Bitmap::try_load_from_file("/res/icons/32x32/app-irc-client.png")); - notification->set_text(message); - notification->show(); -} - -void IRCWindow::did_add_message(const String& name, const String& message) -{ - post_notification_if_needed(name, message); - - if (!is_active()) { - ++m_unread_count; - m_client->aid_update_window_list(); - return; - } - m_page_view->scroll_to_bottom(); -} - -void IRCWindow::clear_unread_count() -{ - if (!m_unread_count) - return; - m_unread_count = 0; - m_client->aid_update_window_list(); -} - -int IRCWindow::unread_count() const -{ - return m_unread_count; -} diff --git a/Userland/Applications/IRCClient/IRCWindow.h b/Userland/Applications/IRCClient/IRCWindow.h deleted file mode 100644 index 0be13eb5f7..0000000000 --- a/Userland/Applications/IRCClient/IRCWindow.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -class IRCChannel; -class IRCClient; -class IRCQuery; -class IRCLogBuffer; - -class IRCWindow : public GUI::Widget { - C_OBJECT(IRCWindow) -public: - enum Type { - Server, - Channel, - Query, - }; - - virtual ~IRCWindow() override; - - String name() const { return m_name; } - void set_name(const String& name) { m_name = name; } - - Type type() const { return m_type; } - - void set_log_buffer(const IRCLogBuffer&); - - bool is_active() const; - - int unread_count() const; - void clear_unread_count(); - - void did_add_message(const String& name = {}, const String& message = {}); - - IRCChannel& channel() { return *(IRCChannel*)m_owner; } - const IRCChannel& channel() const { return *(const IRCChannel*)m_owner; } - - IRCQuery& query() { return *(IRCQuery*)m_owner; } - const IRCQuery& query() const { return *(const IRCQuery*)m_owner; } - -private: - IRCWindow(IRCClient&, void* owner, Type, const String& name); - - void post_notification_if_needed(const String& name, const String& message); - - NonnullRefPtr m_client; - void* m_owner { nullptr }; - Type m_type; - String m_name; - RefPtr m_page_view; - RefPtr m_text_box; - RefPtr m_log_buffer; - RefPtr m_context_menu; - int m_unread_count { 0 }; -}; diff --git a/Userland/Applications/IRCClient/IRCWindowListModel.cpp b/Userland/Applications/IRCClient/IRCWindowListModel.cpp deleted file mode 100644 index 76ef9623f9..0000000000 --- a/Userland/Applications/IRCClient/IRCWindowListModel.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCWindowListModel.h" -#include "IRCChannel.h" -#include "IRCClient.h" - -IRCWindowListModel::IRCWindowListModel(IRCClient& client) - : m_client(client) -{ -} - -IRCWindowListModel::~IRCWindowListModel() -{ -} - -int IRCWindowListModel::row_count(const GUI::ModelIndex&) const -{ - return m_client->window_count(); -} - -int IRCWindowListModel::column_count(const GUI::ModelIndex&) const -{ - return 1; -} - -String IRCWindowListModel::column_name(int column) const -{ - switch (column) { - case Column::Name: - return "Name"; - } - VERIFY_NOT_REACHED(); -} - -GUI::Variant IRCWindowListModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - if (role == GUI::ModelRole::TextAlignment) - return Gfx::TextAlignment::CenterLeft; - if (role == GUI::ModelRole::Display) { - switch (index.column()) { - case Column::Name: { - auto& window = m_client->window_at(index.row()); - if (window.unread_count()) - return String::formatted("{} ({})", window.name(), window.unread_count()); - return window.name(); - } - } - } - if (role == GUI::ModelRole::ForegroundColor) { - switch (index.column()) { - case Column::Name: { - auto& window = m_client->window_at(index.row()); - if (window.unread_count()) - return Color(Color::Red); - if (!window.channel().is_open()) - return Color(Color::WarmGray); - return Color(Color::Black); - } - } - } - return {}; -} diff --git a/Userland/Applications/IRCClient/IRCWindowListModel.h b/Userland/Applications/IRCClient/IRCWindowListModel.h deleted file mode 100644 index 8de806cbcc..0000000000 --- a/Userland/Applications/IRCClient/IRCWindowListModel.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include - -class IRCClient; -class IRCWindow; - -class IRCWindowListModel final : public GUI::Model { -public: - enum Column { - Name, - }; - - static NonnullRefPtr create(IRCClient& client) { return adopt_ref(*new IRCWindowListModel(client)); } - virtual ~IRCWindowListModel() override; - - virtual int row_count(const GUI::ModelIndex&) const override; - virtual int column_count(const GUI::ModelIndex&) const override; - virtual String column_name(int column) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - -private: - explicit IRCWindowListModel(IRCClient&); - - NonnullRefPtr m_client; -}; diff --git a/Userland/Applications/IRCClient/main.cpp b/Userland/Applications/IRCClient/main.cpp deleted file mode 100644 index 1249632f0d..0000000000 --- a/Userland/Applications/IRCClient/main.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "IRCAppWindow.h" -#include "IRCClient.h" -#include -#include -#include -#include -#include - -int main(int argc, char** argv) -{ - if (pledge("stdio inet unix recvfd sendfd cpath rpath wpath", nullptr) < 0) { - perror("pledge"); - return 1; - } - - if (getuid() == 0) { - warnln("Refusing to run as root"); - return 1; - } - - auto app = GUI::Application::construct(argc, argv); - - if (unveil("/tmp/portal/lookup", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/tmp/portal/notify", "rw") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/etc/passwd", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(Core::StandardPaths::home_directory().characters(), "rwc") < 0) { - perror("unveil"); - return 1; - } - - if (unveil("/res", "r") < 0) { - perror("unveil"); - return 1; - } - - if (unveil(nullptr, nullptr) < 0) { - perror("unveil"); - return 1; - } - - URL url = ""; - if (app->args().size() >= 1) { - url = URL::create_with_url_or_path(app->args()[0]); - - if (url.protocol().to_lowercase() == "ircs") { - warnln("Secure IRC over SSL/TLS (ircs) is not supported"); - return 1; - } - - if (url.protocol().to_lowercase() != "irc") { - warnln("Unsupported protocol"); - return 1; - } - - if (url.host().is_empty()) { - warnln("Invalid URL"); - return 1; - } - - if (!url.port() || url.port() == 80) - url.set_port(6667); - } - - auto app_window = IRCAppWindow::construct(url.host(), url.port()); - app_window->show(); - return app->exec(); -} diff --git a/Userland/Demos/WidgetGallery/GalleryWidget.cpp b/Userland/Demos/WidgetGallery/GalleryWidget.cpp index 662057d8e3..badda01419 100644 --- a/Userland/Demos/WidgetGallery/GalleryWidget.cpp +++ b/Userland/Demos/WidgetGallery/GalleryWidget.cpp @@ -131,7 +131,7 @@ GalleryWidget::GalleryWidget() }; m_msgbox_button = basics_tab.find_descendant_of_type_named("msgbox_button"); - m_msgbox_button->set_icon(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/app-irc-client.png")); + m_msgbox_button->set_icon(Gfx::Bitmap::try_load_from_file("/res/icons/16x16/app-browser.png")); m_msgbox_type = GUI::MessageBox::Type::None; m_msgbox_input_type = GUI::MessageBox::InputType::OK;