IRC client
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

irc.c 3.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. /* Copyright (C) 2018 Curtis McEnroe <june@causal.agency>
  2. *
  3. * This program is free software: you can redistribute it and/or modify
  4. * it under the terms of the GNU Affero General Public License as published by
  5. * the Free Software Foundation, either version 3 of the License, or
  6. * (at your option) any later version.
  7. *
  8. * This program is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. * GNU Affero General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Affero General Public License
  14. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <err.h>
  17. #include <fcntl.h>
  18. #include <netdb.h>
  19. #include <netinet/in.h>
  20. #include <stdarg.h>
  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <string.h>
  24. #include <sys/socket.h>
  25. #include <sysexits.h>
  26. #include <tls.h>
  27. #include <unistd.h>
  28. #include "chat.h"
  29. static struct tls *client;
  30. int ircConnect(void) {
  31. int error;
  32. struct tls_config *config = tls_config_new();
  33. error = tls_config_set_ciphers(config, "compat");
  34. if (error) errx(EX_SOFTWARE, "tls_config");
  35. client = tls_client();
  36. if (!client) errx(EX_SOFTWARE, "tls_client");
  37. error = tls_configure(client, config);
  38. if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client));
  39. tls_config_free(config);
  40. struct addrinfo *head;
  41. struct addrinfo hints = {
  42. .ai_family = AF_UNSPEC,
  43. .ai_socktype = SOCK_STREAM,
  44. .ai_protocol = IPPROTO_TCP,
  45. };
  46. error = getaddrinfo(self.host, self.port, &hints, &head);
  47. if (error) errx(EX_NOHOST, "getaddrinfo: %s", gai_strerror(error));
  48. int sock = -1;
  49. for (struct addrinfo *ai = head; ai; ai = ai->ai_next) {
  50. sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
  51. if (sock < 0) err(EX_OSERR, "socket");
  52. error = connect(sock, ai->ai_addr, ai->ai_addrlen);
  53. if (!error) break;
  54. close(sock);
  55. sock = -1;
  56. }
  57. if (sock < 0) err(EX_UNAVAILABLE, "connect");
  58. freeaddrinfo(head);
  59. error = fcntl(sock, F_SETFD, FD_CLOEXEC);
  60. if (error) err(EX_IOERR, "fcntl");
  61. error = tls_connect_socket(client, sock, self.host);
  62. if (error) errx(EX_PROTOCOL, "tls_connect: %s", tls_error(client));
  63. const char *ssh = getenv("SSH_CLIENT");
  64. if (self.webp && ssh) {
  65. int len = strlen(ssh);
  66. const char *sp = strchr(ssh, ' ');
  67. if (sp) len = sp - ssh;
  68. ircFmt(
  69. "WEBIRC %s %s %.*s %.*s\r\n",
  70. self.webp, self.user, len, ssh, len, ssh
  71. );
  72. }
  73. if (self.auth) ircFmt("CAP REQ :sasl\r\n");
  74. if (self.pass) ircFmt("PASS :%s\r\n", self.pass);
  75. ircFmt("NICK %s\r\n", self.nick);
  76. ircFmt("USER %s 0 * :%s\r\n", self.user, self.real);
  77. return sock;
  78. }
  79. void ircWrite(const char *ptr, size_t len) {
  80. while (len) {
  81. ssize_t ret = tls_write(client, ptr, len);
  82. if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT) continue;
  83. if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(client));
  84. ptr += ret;
  85. len -= ret;
  86. }
  87. }
  88. void ircFmt(const char *format, ...) {
  89. char *buf;
  90. va_list ap;
  91. va_start(ap, format);
  92. int len = vasprintf(&buf, format, ap);
  93. va_end(ap);
  94. if (!buf) err(EX_OSERR, "vasprintf");
  95. if (self.raw) {
  96. uiFmt(TagRaw, UICold, "\3%d<<<\3 %.*s", IRCWhite, len - 2, buf);
  97. }
  98. ircWrite(buf, len);
  99. free(buf);
  100. }
  101. void ircRead(void) {
  102. static char buf[4096];
  103. static size_t len;
  104. ssize_t read;
  105. retry:
  106. read = tls_read(client, &buf[len], sizeof(buf) - len);
  107. if (read == TLS_WANT_POLLIN || read == TLS_WANT_POLLOUT) goto retry;
  108. if (read < 0) errx(EX_IOERR, "tls_read: %s", tls_error(client));
  109. if (!read) errx(EX_PROTOCOL, "unexpected eof");
  110. len += read;
  111. char *crlf;
  112. char *line = buf;
  113. while (NULL != (crlf = strnstr(line, "\r\n", &buf[len] - line))) {
  114. crlf[0] = '\0';
  115. if (self.raw) {
  116. uiFmt(TagRaw, UICold, "\3%d>>>\3 %s", IRCGray, line);
  117. }
  118. handle(line);
  119. line = &crlf[2];
  120. }
  121. len -= line - buf;
  122. memmove(buf, line, len);
  123. }