IRCClient.cpp 10 KB

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