* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#define _GNU_SOURCE
-
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
-#include <errno.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <arpa/inet.h>
-#include <netinet/tcp.h>
-#include <netdb.h>
#include "util.h"
#include "conf.h"
#include "chat.h"
+#include "net.h"
-/* IRC Constants */
-#define BUF_LEN 1024
+/* IRC constants */
+#define IRC_LINE 512
/* IRC types */
typedef enum {
const char *auth;
const char *pass;
+ net_t net;
irc_state_t state;
- poll_t poll;
-
- char in_buf[BUF_LEN];
- int in_len;
- char out_buf[BUF_LEN];
- int out_pos;
- int out_len;
+ char line[IRC_LINE];
+ int pos;
struct irc_server_t *next;
} irc_server_t;
return cur;
}
-static int send_line(irc_server_t *srv, const char *fmt, ...)
-{
- int len, sent;
- va_list ap;
- if (srv->out_len)
- return 0;
-
- va_start(ap, fmt);
- len = vsnprintf(srv->out_buf, BUF_LEN, fmt, ap);
- va_end(ap);
- if (len <= 0)
- return 0;
- if (len >= BUF_LEN)
- len = BUF_LEN-1;
- srv->out_buf[len] = '\n';
- len++;
-
- sent = send(srv->poll.fd, srv->out_buf, len, 0);
- if (sent == len)
- return 1;
-
- srv->out_len = len;
- srv->out_pos = sent;
- return 0;
-}
-
static void recv_line(irc_server_t *srv, const char *line)
{
- static char src[BUF_LEN]; // (:([^ ]+) +)?
- static char cmd[BUF_LEN]; // (([A-Z0-9]+) +)
- static char dst[BUF_LEN]; // (([^ ]+)[= ]+)?
- static char arg[BUF_LEN]; // (([^: ]+) *)?
- static char msg[BUF_LEN]; // (:(.*))?
- static char from[BUF_LEN];
+ static char src[IRC_LINE]; // (:([^ ]+) +)?
+ static char cmd[IRC_LINE]; // (([A-Z0-9]+) +)
+ static char dst[IRC_LINE]; // (([^ ]+)[= ]+)?
+ static char arg[IRC_LINE]; // (([^: ]+) *)?
+ static char msg[IRC_LINE]; // (:(.*))?
+ static char from[IRC_LINE];
int i;
const char *c = line;
- // Clear strings
+ /* Clear strings */
src[0] = cmd[0] = dst[0] = arg[0] = msg[0] = from[0] = '\0';
- // Read src
+ /* Read src */
if (*c == ':') {
c++;
for (i = 0; *c && *c != ' '; i++)
from[i] = '\0';
}
- // Read cmd
+ /* Read cmd */
for (i = 0; isalnum(*c); i++)
cmd[i] = *c++;
while (*c == ' ')
c++;
cmd[i] = '\0';
- // Read dst
+ /* Read dst */
if ((*c && *c != ' ') &&
(strchr(c+1, ' ') || strchr(c+1, '='))) {
for (i = 0; *c && *c != ' '; i++)
dst[i] = '\0';
}
- // Read arg
+ /* Read arg */
if (*c && *c != ':' && *c != ' ') {
for (i = 0; *c && *c != ' '; i++)
arg[i] = *c++;
arg[i] = '\0';
}
- // Read msg
+ /* Read msg */
if (*c == ':') {
c++;
for (i = 0; *c; i++)
//debug(" arg %s", arg);
//debug(" msg %s", msg);
- // Parse messages
+ /* Parse messages */
if (match(cmd, "001") || strstr(msg, "Welcome"))
srv->state = IRC_JOIN;
if (match(cmd, "PING"))
- send_line(srv, "PING %s", msg);
+ net_print(&srv->net, "PING %s\n", msg);
if (match(cmd, "PRIVMSG"))
chat_recv(dst, from, msg);
+
+ /* Notices */
+ if (match(cmd, "NOTICE") ||
+ match(cmd, "001") ||
+ match(cmd, "372") ||
+ match(cmd, "375") ||
+ match(cmd, "376"))
+ chat_notice(from, NULL, "%s", msg);
}
-static void on_poll(void *_srv)
+static void on_recv(void *_srv, char *buf, int len)
{
- static char buf[BUF_LEN];
- static char hostname[512];
-
irc_server_t *srv = _srv;
- int len;
/* Handle Input */
- len = recv(srv->poll.fd, buf, sizeof(buf), 0);
- if (len < 0) {
- debug("recv: error: %s -- %s", srv->name, strerror(errno));
- }
- if (len == 0) {
- debug("recv: close: %s", srv->name);
- srv->state = IRC_DEAD;
- poll_del(&srv->poll);
- return;
- }
- if (len > 0) {
- debug("recv: ready: %s -- [%.*s]", srv->name, len, buf);
- }
for (int i = 0; i < len; i++) {
- if (srv->in_len < BUF_LEN) {
- srv->in_buf[srv->in_len] = buf[i];
- srv->in_len++;
+ if (srv->pos < IRC_LINE) {
+ srv->line[srv->pos] = buf[i];
+ srv->pos++;
}
if (buf[i] == '\n' || buf[i] == '\r') {
- srv->in_buf[srv->in_len-1] = '\0';
- if (srv->in_len > 1)
- recv_line(srv, srv->in_buf);
- srv->in_len = 0;
+ srv->line[srv->pos-1] = '\0';
+ if (srv->pos > 1)
+ recv_line(srv, srv->line);
+ srv->pos = 0;
}
}
- /* Handle Output */
- if (srv->out_len > 0) {
- len = send(srv->poll.fd, &srv->out_buf[srv->out_pos],
- srv->out_len-srv->out_pos, 0);
- if (len > 0)
- srv->out_pos += len;
- if (srv->out_pos == srv->out_len) {
- srv->out_pos = 0;
- srv->out_len = 0;
- }
- }
-
- /* Handle Errors */
- int err = 0;
- socklen_t elen = sizeof(err);
- if (getsockopt(srv->poll.fd, SOL_SOCKET, SO_ERROR, &err, &elen))
- error("Error getting socket opt");
- if (err) {
- debug("disconnect: %s -- %s", srv->name, strerror(err));
- srv->state = IRC_DEAD;
- poll_del(&srv->poll);
- return;
- }
-
/* State machine */
if (srv->state == IRC_USER) {
- if (gethostname(hostname, sizeof(hostname)))
- error("Error getting hostname");
- if (send_line(srv, "USER %s %s %s :%s",
+ if (net_print(&srv->net, "USER %s %s %s :%s\n",
getenv("USER") ?: "lameuser",
- hostname, srv->host, srv->nick))
+ get_hostname(), srv->host, srv->nick))
srv->state = IRC_NICK;
}
if (srv->state == IRC_NICK) {
- if (send_line(srv, "NICK %s", srv->nick))
+ if (net_print(&srv->net, "NICK %s\n", srv->nick))
srv->state = IRC_READY;
}
if (srv->state == IRC_JOIN) {
for (irc_channel_t *chan = channels; chan; chan = chan->next)
if (chan->join)
- send_line(srv, "JOIN %s", chan->channel);
+ net_print(&srv->net, "JOIN %s\n", chan->channel);
srv->state = IRC_READY;
}
-
- /* Enable output poll */
- if (srv->out_len)
- poll_ctl(&srv->poll, 1, 1, 1);
- else
- poll_ctl(&srv->poll, 1, 0, 1);
}
static void irc_connect(irc_server_t *srv)
{
- int sock, flags;
- struct addrinfo *addrs = NULL;
- struct addrinfo hints = {};
- char service[16];
-
- snprintf(service, sizeof(service), "%d", srv->port);
- hints.ai_family = AF_INET;
- hints.ai_socktype = SOCK_STREAM;
-
- /* Setup address */
- if (getaddrinfo(srv->host, service, &hints, &addrs))
- error("Error getting address info");
-
- if ((sock = socket(addrs->ai_family,
- addrs->ai_socktype,
- addrs->ai_protocol)) < 0)
- error("Error opening irc socket");
-
- if ((flags = fcntl(sock, F_GETFL, 0)) < 0)
- error("Error getting irc socket flags");
-
- if (fcntl(sock, F_SETFL, flags|O_NONBLOCK) < 0)
- error("Error setting irc socket non-blocking");
-
- if (connect(sock, addrs->ai_addr, addrs->ai_addrlen) < 0)
- if (errno != EINPROGRESS)
- error("Error connecting socket");
-
- freeaddrinfo(addrs);
+ /* Net connect */
+ srv->net.recv = on_recv;
+ srv->net.data = srv;
+ net_open(&srv->net, srv->host, srv->port);
/* Setup server */
srv->state = IRC_USER;
- poll_add(&srv->poll, sock, on_poll, srv);
}
/* IRC functions */
irc_server_t *srv = find_server(chan->server, 0);
if (!srv)
return;
- send_line(srv, "PRIVMSG %s :%s", chan->channel, msg);
+ net_print(&srv->net, "PRIVMSG %s :%s\n", chan->channel, msg);
}
void irc_exit(void)
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
-#include <string.h>
#include <errno.h>
+#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
-#include <expat.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
}
}
-void net_encrypt(net_t *net)
+/* Networking functions */
+const char *get_hostname(void)
{
- debug("net: encrypt");
-
- net->ctx = SSL_CTX_new(TLSv1_2_client_method());
- net->ssl = SSL_new(net->ctx);
-
- net->in = BIO_new(BIO_s_mem());
- net->out = BIO_new(BIO_s_mem());
+ static char hostname[512];
- BIO_set_mem_eof_return(net->in, -1);
- BIO_set_mem_eof_return(net->out, -1);
+ if (gethostname(hostname, sizeof(hostname)))
+ error("Error getting hostname");
- SSL_set_bio(net->ssl, net->in, net->out);
- SSL_set_connect_state(net->ssl);
-
- net->state = NET_HANDSHAKE;
+ return hostname;
}
-void net_close(net_t *net)
+void net_init(void)
{
- debug("net_close: %s:%d",
- net->host, net->port);
- net->state = NET_CLOSED;
- poll_del(&net->poll);
+ SSL_library_init();
+ SSL_load_error_strings();
+ ERR_load_BIO_strings();
+ OpenSSL_add_all_algorithms();
}
void net_open(net_t *net, const char *host, int port)
poll_add(&net->poll, sock, on_poll, net);
}
+void net_encrypt(net_t *net)
+{
+ debug("net: encrypt");
+
+ net->ctx = SSL_CTX_new(TLSv1_2_client_method());
+ net->ssl = SSL_new(net->ctx);
+
+ net->in = BIO_new(BIO_s_mem());
+ net->out = BIO_new(BIO_s_mem());
+
+ BIO_set_mem_eof_return(net->in, -1);
+ BIO_set_mem_eof_return(net->out, -1);
+
+ SSL_set_bio(net->ssl, net->in, net->out);
+ SSL_set_connect_state(net->ssl);
+
+ net->state = NET_HANDSHAKE;
+}
+
int net_send(net_t *net, const char *buf, int len)
{
if (net->out_len)
if (len > NET_BUFFER)
len = NET_BUFFER;
- debug("net: print [%.*s]", len, net->out_buf);
+ if (net->out_buf[len-1] == '\n')
+ debug("net: print [%.*s]", len-1, net->out_buf);
+ else
+ debug("net: print [%.*s]", len, net->out_buf);
net->out_len = len;
net->out_pos = 0;
return flush(net);
}
-void net_init(void)
+void net_close(net_t *net)
{
- SSL_library_init();
- SSL_load_error_strings();
- ERR_load_BIO_strings();
- OpenSSL_add_all_algorithms();
+ debug("net_close: %s:%d",
+ net->host, net->port);
+ net->state = NET_CLOSED;
+ poll_del(&net->poll);
}
+
#include <stdarg.h>
#include <string.h>
#include <errno.h>
-#include <fcntl.h>
-#include <netdb.h>
#include <expat.h>
#include "util.h"
-#include "chat.h"
#include "conf.h"
+#include "chat.h"
#include "net.h"
/* Constants */
if (len > 0)
XML_Parse(srv->expat, buf, len, 0);
+ /* Stream restart */
+ if (srv->state == XMPP_ENCRYPT) {
+ /* Encrypt connection */
+ net_encrypt(&srv->net);
+
+ /* Reset Expat */
+ if (!(XML_ParserReset(srv->expat, NULL)))
+ error("Error resetting XML parser");
+ XML_SetUserData(srv->expat, srv);
+ XML_SetStartElementHandler(srv->expat, on_start);
+ XML_SetEndElementHandler(srv->expat, on_end);
+ XML_SetCharacterDataHandler(srv->expat, on_data);
+
+ /* Reset server */
+ debug("xmpp: encrypt -> stream");
+ srv->state = XMPP_SEND_STREAM;
+ }
+ if (srv->state == XMPP_RESTART) {
+ /* Reset Expat */
+ if (!(XML_ParserReset(srv->expat, NULL)))
+ error("Error resetting XML parser");
+ XML_SetUserData(srv->expat, srv);
+ XML_SetStartElementHandler(srv->expat, on_start);
+ XML_SetEndElementHandler(srv->expat, on_end);
+ XML_SetCharacterDataHandler(srv->expat, on_data);
+
+ /* Reset server */
+ debug("xmpp: restart -> stream");
+ srv->state = XMPP_SEND_STREAM;
+ }
+
/* State machine */
-output:
if (srv->state == XMPP_SEND_STREAM) {
if (net_print(&srv->net,
"<?xml version='1.0'?>"
}
srv->state = XMPP_READY;
}
- if (srv->state == XMPP_ENCRYPT) {
- /* Encrypt connection */
- net_encrypt(&srv->net);
-
- /* Reset Expat */
- if (!(XML_ParserReset(srv->expat, NULL)))
- error("Error resetting XML parser");
- XML_SetUserData(srv->expat, srv);
- XML_SetStartElementHandler(srv->expat, on_start);
- XML_SetEndElementHandler(srv->expat, on_end);
- XML_SetCharacterDataHandler(srv->expat, on_data);
-
- /* Reset server */
- debug("xmpp: encrypt -> stream");
- srv->state = XMPP_SEND_STREAM;
- goto output;
- }
- if (srv->state == XMPP_RESTART) {
- /* Reset Expat */
- if (!(XML_ParserReset(srv->expat, NULL)))
- error("Error resetting XML parser");
- XML_SetUserData(srv->expat, srv);
- XML_SetStartElementHandler(srv->expat, on_start);
- XML_SetEndElementHandler(srv->expat, on_end);
- XML_SetCharacterDataHandler(srv->expat, on_data);
-
- /* Reset server */
- debug("xmpp: restart -> stream");
- srv->state = XMPP_SEND_STREAM;
- goto output;
- }
}
static void xmpp_connect(xmpp_server_t *srv)