IRCClient.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. #include "IRCClient.h"
  2. #include "IRCChannel.h"
  3. #include "IRCQuery.h"
  4. #include "IRCLogBuffer.h"
  5. #include "IRCClientWindow.h"
  6. #include "IRCClientWindowListModel.h"
  7. #include <LibGUI/GNotifier.h>
  8. #include <sys/socket.h>
  9. #include <netinet/in.h>
  10. #include <arpa/inet.h>
  11. #include <unistd.h>
  12. #include <stdio.h>
  13. enum IRCNumeric {
  14. RPL_NAMREPLY = 353,
  15. RPL_ENDOFNAMES = 366,
  16. };
  17. IRCClient::IRCClient(const String& address, int port)
  18. : m_hostname(address)
  19. , m_port(port)
  20. , m_nickname("anon")
  21. , m_log(IRCLogBuffer::create())
  22. {
  23. m_client_window_list_model = new IRCClientWindowListModel(*this);
  24. }
  25. IRCClient::~IRCClient()
  26. {
  27. }
  28. bool IRCClient::connect()
  29. {
  30. if (m_socket_fd != -1) {
  31. ASSERT_NOT_REACHED();
  32. }
  33. m_socket_fd = socket(AF_INET, SOCK_STREAM, 0);
  34. if (m_socket_fd < 0) {
  35. perror("socket");
  36. exit(1);
  37. }
  38. struct sockaddr_in addr;
  39. memset(&addr, 0, sizeof(addr));
  40. addr.sin_family = AF_INET;
  41. addr.sin_port = htons(m_port);
  42. int rc = inet_pton(AF_INET, m_hostname.characters(), &addr.sin_addr);
  43. if (rc < 0) {
  44. perror("inet_pton");
  45. exit(1);
  46. }
  47. printf("Connecting to %s...", m_hostname.characters());
  48. fflush(stdout);
  49. rc = ::connect(m_socket_fd, (struct sockaddr*)&addr, sizeof(addr));
  50. if (rc < 0) {
  51. perror("connect");
  52. exit(1);
  53. }
  54. printf("ok!\n");
  55. m_notifier = make<GNotifier>(m_socket_fd, GNotifier::Read);
  56. m_notifier->on_ready_to_read = [this] (GNotifier&) { receive_from_server(); };
  57. send_user();
  58. send_nick();
  59. if (on_connect)
  60. on_connect();
  61. return true;
  62. }
  63. void IRCClient::receive_from_server()
  64. {
  65. char buffer[4096];
  66. int nread = recv(m_socket_fd, buffer, sizeof(buffer) - 1, 0);
  67. if (nread < 0) {
  68. perror("recv");
  69. exit(1);
  70. }
  71. if (nread == 0) {
  72. printf("IRCClient: Connection closed!\n");
  73. exit(1);
  74. }
  75. buffer[nread] = '\0';
  76. #if 0
  77. printf("Received: '%s'\n", buffer);
  78. #endif
  79. for (int i = 0; i < nread; ++i) {
  80. char ch = buffer[i];
  81. if (ch == '\r')
  82. continue;
  83. if (ch == '\n') {
  84. process_line();
  85. m_line_buffer.clear_with_capacity();
  86. continue;
  87. }
  88. m_line_buffer.append(ch);
  89. }
  90. }
  91. void IRCClient::process_line()
  92. {
  93. Message msg;
  94. Vector<char> prefix;
  95. Vector<char> command;
  96. Vector<char> current_parameter;
  97. enum {
  98. Start,
  99. InPrefix,
  100. InCommand,
  101. InStartOfParameter,
  102. InParameter,
  103. InTrailingParameter,
  104. } state = Start;
  105. for (char ch : m_line_buffer) {
  106. switch (state) {
  107. case Start:
  108. if (ch == ':') {
  109. state = InPrefix;
  110. continue;
  111. }
  112. state = InCommand;
  113. [[fallthrough]];
  114. case InCommand:
  115. if (ch == ' ') {
  116. state = InStartOfParameter;
  117. continue;
  118. }
  119. command.append(ch);
  120. continue;
  121. case InPrefix:
  122. if (ch == ' ') {
  123. state = InCommand;
  124. continue;
  125. }
  126. prefix.append(ch);
  127. continue;
  128. case InStartOfParameter:
  129. if (ch == ':') {
  130. state = InTrailingParameter;
  131. continue;
  132. }
  133. state = InParameter;
  134. [[fallthrough]];
  135. case InParameter:
  136. if (ch == ' ') {
  137. if (!current_parameter.is_empty())
  138. msg.arguments.append(String(current_parameter.data(), current_parameter.size()));
  139. current_parameter.clear_with_capacity();
  140. state = InStartOfParameter;
  141. continue;
  142. }
  143. current_parameter.append(ch);
  144. continue;
  145. case InTrailingParameter:
  146. current_parameter.append(ch);
  147. continue;
  148. }
  149. }
  150. if (!current_parameter.is_empty())
  151. msg.arguments.append(String(current_parameter.data(), current_parameter.size()));
  152. msg.prefix = String(prefix.data(), prefix.size());
  153. msg.command = String(command.data(), command.size());
  154. handle(msg, String(m_line_buffer.data(), m_line_buffer.size()));
  155. }
  156. void IRCClient::send(const String& text)
  157. {
  158. int rc = ::send(m_socket_fd, text.characters(), text.length(), 0);
  159. if (rc < 0) {
  160. perror("send");
  161. exit(1);
  162. }
  163. }
  164. void IRCClient::send_user()
  165. {
  166. send(String::format("USER %s 0 * :%s\r\n", m_nickname.characters(), m_nickname.characters()));
  167. }
  168. void IRCClient::send_nick()
  169. {
  170. send(String::format("NICK %s\r\n", m_nickname.characters()));
  171. }
  172. void IRCClient::send_pong(const String& server)
  173. {
  174. send(String::format("PONG %s\r\n", server.characters()));
  175. sleep(1);
  176. }
  177. void IRCClient::join_channel(const String& channel_name)
  178. {
  179. send(String::format("JOIN %s\r\n", channel_name.characters()));
  180. }
  181. void IRCClient::handle(const Message& msg, const String& verbatim)
  182. {
  183. printf("IRCClient::execute: prefix='%s', command='%s', arguments=%d\n",
  184. msg.prefix.characters(),
  185. msg.command.characters(),
  186. msg.arguments.size()
  187. );
  188. int i = 0;
  189. for (auto& arg : msg.arguments) {
  190. printf(" [%d]: %s\n", i, arg.characters());
  191. ++i;
  192. }
  193. bool is_numeric;
  194. int numeric = msg.command.to_uint(is_numeric);
  195. if (is_numeric) {
  196. switch (numeric) {
  197. case RPL_NAMREPLY:
  198. handle_namreply(msg);
  199. return;
  200. }
  201. }
  202. if (msg.command == "PING")
  203. return handle_ping(msg);
  204. if (msg.command == "JOIN")
  205. return handle_join(msg);
  206. if (msg.command == "PRIVMSG")
  207. return handle_privmsg(msg);
  208. if (msg.arguments.size() >= 2)
  209. m_log->add_message(0, "Server", String::format("[%s] %s", msg.command.characters(), msg.arguments[1].characters()));
  210. }
  211. bool IRCClient::is_nick_prefix(char ch) const
  212. {
  213. switch (ch) {
  214. case '@':
  215. case '+':
  216. case '~':
  217. case '&':
  218. case '%':
  219. return true;
  220. }
  221. return false;
  222. }
  223. void IRCClient::handle_privmsg(const Message& msg)
  224. {
  225. if (msg.arguments.size() < 2)
  226. return;
  227. if (msg.prefix.is_empty())
  228. return;
  229. auto parts = msg.prefix.split('!');
  230. auto sender_nick = parts[0];
  231. auto target = msg.arguments[0];
  232. printf("handle_privmsg: sender_nick='%s', target='%s'\n", sender_nick.characters(), target.characters());
  233. if (sender_nick.is_empty())
  234. return;
  235. char sender_prefix = 0;
  236. if (is_nick_prefix(sender_nick[0])) {
  237. sender_prefix = sender_nick[0];
  238. sender_nick = sender_nick.substring(1, sender_nick.length() - 1);
  239. }
  240. {
  241. auto it = m_channels.find(target);
  242. if (it != m_channels.end()) {
  243. (*it).value->add_message(sender_prefix, sender_nick, msg.arguments[1]);
  244. if (on_channel_message)
  245. on_channel_message(target);
  246. return;
  247. }
  248. }
  249. auto& query = ensure_query(sender_nick);
  250. query.add_message(sender_prefix, sender_nick, msg.arguments[1]);
  251. if (on_query_message)
  252. on_query_message(target);
  253. }
  254. IRCQuery& IRCClient::ensure_query(const String& name)
  255. {
  256. auto it = m_queries.find(name);
  257. if (it != m_queries.end())
  258. return *(*it).value;
  259. auto query = IRCQuery::create(*this, name);
  260. auto& query_reference = *query;
  261. m_queries.set(name, query.copy_ref());
  262. return query_reference;
  263. }
  264. void IRCClient::handle_ping(const Message& msg)
  265. {
  266. if (msg.arguments.size() < 0)
  267. return;
  268. m_log->add_message(0, "", String::format("Ping? Pong! %s\n", msg.arguments[0].characters()));
  269. send_pong(msg.arguments[0]);
  270. }
  271. void IRCClient::handle_join(const Message& msg)
  272. {
  273. if (msg.arguments.size() != 1)
  274. return;
  275. auto& channel_name = msg.arguments[0];
  276. auto it = m_channels.find(channel_name);
  277. ASSERT(it == m_channels.end());
  278. auto channel = IRCChannel::create(*this, channel_name);
  279. m_channels.set(channel_name, move(channel));
  280. if (on_join)
  281. on_join(channel_name);
  282. }
  283. void IRCClient::handle_namreply(const Message& msg)
  284. {
  285. printf("NAMREPLY:\n");
  286. if (msg.arguments.size() < 4)
  287. return;
  288. auto& channel_name = msg.arguments[2];
  289. auto it = m_channels.find(channel_name);
  290. if (it == m_channels.end()) {
  291. fprintf(stderr, "Warning: Got RPL_NAMREPLY for untracked channel %s\n", channel_name.characters());
  292. return;
  293. }
  294. auto& channel = *(*it).value;
  295. auto members = msg.arguments[3].split(' ');
  296. for (auto& member : members) {
  297. if (member.is_empty())
  298. continue;
  299. char prefix = 0;
  300. if (is_nick_prefix(member[0]))
  301. prefix = member[0];
  302. channel.add_member(member, prefix);
  303. }
  304. channel.dump();
  305. }
  306. void IRCClient::register_subwindow(IRCClientWindow& subwindow)
  307. {
  308. if (subwindow.type() == IRCClientWindow::Server) {
  309. m_server_subwindow = &subwindow;
  310. subwindow.set_log_buffer(*m_log);
  311. } else if (subwindow.type() == IRCClientWindow::Channel) {
  312. auto it = m_channels.find(subwindow.name());
  313. ASSERT(it != m_channels.end());
  314. auto& channel = *(*it).value;
  315. subwindow.set_log_buffer(channel.log());
  316. } else if (subwindow.type() == IRCClientWindow::Query) {
  317. subwindow.set_log_buffer(ensure_query(subwindow.name()).log());
  318. }
  319. m_windows.append(&subwindow);
  320. m_client_window_list_model->update();
  321. }
  322. void IRCClient::unregister_subwindow(IRCClientWindow& subwindow)
  323. {
  324. if (subwindow.type() == IRCClientWindow::Server) {
  325. m_server_subwindow = &subwindow;
  326. }
  327. for (int i = 0; i < m_windows.size(); ++i) {
  328. if (m_windows.at(i) == &subwindow) {
  329. m_windows.remove(i);
  330. break;
  331. }
  332. }
  333. m_client_window_list_model->update();
  334. }