IRCClient.cpp 9.3 KB

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