#include #include #include #include #include #include "event.h" #include "ircsession.h" #include "linebuffer.h" #include "logging.h" #include "sockaddr.h" #include "tcpsocket.h" struct irc_session * irc_create_session(void) { struct irc_session *session = malloc(sizeof(struct irc_session)); if (session == NULL) return NULL; memset(session, 0, sizeof(struct irc_session)); session->state = IRC_NONE; session->inbuf = create_linebuffer(4096); session->outbuf = create_linebuffer(4096); return session; } void irc_destroy_session(struct irc_session *session) { linebuffer_free(session->outbuf); linebuffer_free(session->inbuf); free(session); } static int irc_write_cb(int fd, void *privdata) { struct irc_session *session = (struct irc_session *)privdata; linebuffer_writefd(session->outbuf, fd); /* remove write fd again */ event_add_writefd(session->handler, 0, NULL, NULL); return 0; } int irc_send(struct irc_session *session, const char *fmt, ...) { va_list az; va_start(az, fmt); linebuffer_vprintf(session->outbuf, fmt, az); va_end (az); linebuffer_put(session->outbuf, "\r\n", 2); /* schedule a write */ event_add_writefd(session->handler, 0, irc_write_cb, session); return 0; } static int irc_read_cb(int fd, void *privdata) { struct irc_session *session = (struct irc_session *)privdata; int len = linebuffer_readfd(session->inbuf, fd); if (len <= 0) { session->state = IRC_DISCONNECTED; log_print(LOG_DEBUG, "irc_read_cb(): disconnected from %s", get_sockaddr_buf(&session->srv_addr)); return -1; } char *line; while ((line = linebuffer_getline(session->inbuf, &len)) != NULL) { char *p = line; char *prefix = NULL; log_print(LOG_DEBUG, "irc_read_cb(): from %s (%d): %s", get_sockaddr_buf(&session->srv_addr), len, line); if (line[0] == ':' ) { while ( *p && *p != ' ') p++; *p++ = '\0'; prefix = line +1; } int code = 0; char *command = NULL; if (isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])) { p[3] = '\0'; code = atoi(p); p += 4; } else { command = p; while ( *p && *p != ' ') p++; *p++ = '\0'; } if (strncmp(line, "PING", 4) == 0) { irc_send(session, "PONG %s", line +6); linebuffer_freeline(session->inbuf); continue; } if (code != 0) { if ((code == 376 || code == 422) && session->state == IRC_CONNECTED) { session->state = IRC_MOTD_RECEIVED; linebuffer_freeline(session->inbuf); if (session->channel_key) return irc_send(session, "JOIN %s :%s", session->channel, session->channel_key); else return irc_send(session, "JOIN %s", session->channel); } } else { if (!strcmp(command, "JOIN")) session->state = IRC_JOINED; } linebuffer_freeline(session->inbuf); } return 0; } static int irc_connect_cb(int fd, void *privdata) { struct irc_session *session = (struct irc_session *)privdata; if (tcp_connect_error(fd)) { session->state = IRC_CONNECTION_FAILED; log_print(LOG_DEBUG, "irc_connect_cb(): failed to connect to %s", get_sockaddr_buf(&session->srv_addr)); return -1; } session->state = IRC_CONNECTED; event_add_readfd(session->handler, 0, irc_read_cb, session); event_add_writefd(session->handler, 0, NULL, NULL); log_print(LOG_DEBUG, "irc_connect_cb(): connected to %s", get_sockaddr_buf(&session->srv_addr)); if (session->server_pass != NULL) irc_send(session, "PASS %s", session->server_pass); irc_send(session, "NICK %s", session->nickname); irc_send(session, "USER %s unknown unknown :%s", session->username ? session->username : "nobody", session->realname ? session->realname : "noname"); return 0; } int irc_connect(struct irc_session *session) { /* TODO: check state before connecting */ session->sock = tcp_connect_nonblock(&session->srv_addr); if (session->sock < 0) return -1; session->state = IRC_CONNECTING; session->handler = event_add_writefd(NULL, session->sock, irc_connect_cb, session); log_print(LOG_DEBUG, "irc_connect(): connecting to %s", get_sockaddr_buf(&session->srv_addr)); return 0; }