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.
170 lines
4.0 KiB
170 lines
4.0 KiB
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
|
|
#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;
|
|
}
|