diff --git a/irc.c b/irc.c new file mode 100644 index 0000000..1c553b0 --- /dev/null +++ b/irc.c @@ -0,0 +1,161 @@ +#include "irc.h" + +unsigned int IRCC_connect(IRCC_client *irc, const char *ip, const unsigned int port){ + struct hostent *ghbn = gethostbyname(ip); + if (!ghbn) + return IRCC_ERROR; + + //Only ipv4 + struct sockaddr_in client_str; + memset(&client_str, 0, sizeof(client_str)); + client_str.sin_family = AF_INET; + client_str.sin_port = htons(port); + client_str.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr *)ghbn->h_addr)); + + irc->socket = socket(AF_INET, SOCK_STREAM, 0); + if (irc->socket < 0) + return IRCC_ERROR; + + int status = connect(irc->socket, (struct sockaddr *)&client_str, sizeof(client_str)); + if (status == -1) + return IRCC_ERROR; + + return IRCC_SUCCESS; +} + +unsigned int IRCC_register(IRCC_client *irc, const char *nickname){ + size_t size = strlen("NICK %s\r\nUSER %s 0 localhost :%s \r\n") + (strlen(nickname) * 3); + char *tmp = (char *)malloc(size); + if (tmp == NULL){ + fprintf(stderr, "malloc returned NULL (IRCC_register)\n"); + exit(1); + } + + sleep(2); + snprintf(tmp, size, "NICK %s\r\nUSER %s 0 localhost :%s\r\n", nickname, nickname, nickname); + send(irc->socket, tmp, strlen(tmp), 0); + + free(tmp); + return IRCC_SUCCESS; +} + +void IRCC_parse(char *tmp, IRCC_client *irc){ + irc->raw[strcspn(irc->raw, "\r\n")] = '\0'; + if (tmp != NULL){ + //Message + if (strstr(tmp, ":") != NULL) + irc->msg = strstr(tmp, ":"); + + //Channel + if (strstr(tmp, " ") != NULL){ + irc->channel = strstr(tmp, " ") + 1; + irc->channel[strcspn(irc->channel, ":") - 1] = '\0'; + } + + //Nickname + irc->raw[strcspn(irc->raw, "!")] = '\0'; + irc->nick = irc->raw + 1; + } +} + +unsigned int IRCC_recv(IRCC_client *irc){ + memset(irc->raw, '\0', irc->size); + irc->channel = NULL; + irc->msg = NULL; + irc->nick = NULL; + + int msg_size = recv(irc->socket, irc->raw, irc->size, 0); + if (msg_size == 0 || msg_size == -1) + return IRCC_DISCONNECTED; + + else if (!strncmp(irc->raw, "PING", 4)){ + *(strchr(irc->raw, 'I')) = 'O'; + send(irc->socket, irc->raw, strlen(irc->raw), 0); + return IRCC_PING; + } + + else { + //puts(irc->raw); + + //Check end of motd + if (strstr(irc->raw, "PRIVMSG ") == NULL && strstr(irc->raw, "MOTD")) + return IRCC_CONNECTED; + + //Other + else if (strstr(irc->raw, "PRIVMSG ")){ + IRCC_parse(strstr(irc->raw, "PRIVMSG "), irc); + return IRCC_PRIVMSG; + } + + else if (strstr(irc->raw, "NICK ")){ + IRCC_parse(strstr(irc->raw, "NICK "), irc); + return IRCC_NICK; + } + + else if (strstr(irc->raw, "TOPIC ")){ + IRCC_parse(strstr(irc->raw, "TOPIC "), irc); + return IRCC_TOPIC; + } + + else if (strstr(irc->raw, "MODE ")){ + IRCC_parse(strstr(irc->raw, "MODE "), irc); + return IRCC_MODE; + } + + else if (strstr(irc->raw, "JOIN ")){ + IRCC_parse(strstr(irc->raw, "JOIN "), irc); + return IRCC_JOIN; + } + + else if (strstr(irc->raw, "PART ") || strstr(irc->raw, "QUIT ")){ + IRCC_parse(strstr(irc->raw, "PART "), irc); + IRCC_parse(strstr(irc->raw, "QUIT "), irc); + return IRCC_PART; + } + } + + return IRCC_SUCCESS; +} + +void IRCC_send(IRCC_client *irc, char *msg, char *channel){ + size_t size = strlen("PRIVMSG :\r\n") + strlen(channel) + strlen(msg); + + char *tmp = (char *)malloc(size + 1); + if (tmp == NULL){ + fprintf(stderr, "malloc returned NULL (IRCC_send)\n"); + exit(1); + } + + snprintf(tmp, size, "PRIVMSG %s :%s\r\n", channel, msg); + send(irc->socket, tmp, strlen(tmp), 0); + + memset(tmp, 0, size); + free(tmp); +} + +unsigned int IRCC_init(IRCC_client *irc, size_t size){ + if (size <= 250){ + fprintf(stderr, "Low buffer size (IRCC_init) (Min 250/512)\n"); + return IRCC_ERROR; + } + + irc->raw = (char *)malloc(size + 1); + if (irc->raw == NULL){ + fprintf(stderr, "malloc returned NULL (IRCC_init)\n"); + exit(1); + } + + irc->msg = irc->nick = irc->channel = NULL; + + irc->size = size; + return IRCC_SUCCESS; +} + +void IRCC_close(IRCC_client *irc){ + close(irc->socket); + + free(irc->raw); + irc->raw = irc->msg = irc->nick = irc->channel = NULL; + + irc->size = 0; +} diff --git a/irc.h b/irc.h new file mode 100644 index 0000000..8f2ffca --- /dev/null +++ b/irc.h @@ -0,0 +1,51 @@ +/* + LICENSE: MIT + Simple libary for working with ipv4 irc servers + Dont support: + ssl + ddc + ipv6 + motd parsing +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#define IRCC_MSG_MAX 512 +#define IRCC_PING_TIMEOUT 600 + +enum { + IRCC_NICK = 1, + IRCC_PRIVMSG, + IRCC_JOIN, + IRCC_PART, + IRCC_PING, + IRCC_TOPIC, + IRCC_MODE, + + IRCC_CONNECTED, + IRCC_DISCONNECTED, + IRCC_ERROR, + IRCC_SUCCESS +}; + +typedef struct { + int socket; + size_t size; + char *raw; + char *msg; + char *channel; + char *nick; +} IRCC_client; + +unsigned int IRCC_connect(IRCC_client *irc, const char *ip, const unsigned int port); +unsigned int IRCC_register(IRCC_client *irc, const char *nickname); +unsigned int IRCC_recv(IRCC_client *irc); +void IRCC_send(IRCC_client *irc, char *msg, char *channel); +unsigned int IRCC_init(IRCC_client *irc, size_t size); +void IRCC_close(IRCC_client *irc); diff --git a/main.c b/main.c new file mode 100644 index 0000000..5d06f5a --- /dev/null +++ b/main.c @@ -0,0 +1,81 @@ +#include "irc.h" + +//Config +#define HOST "localhost" +#define PORT 6667 +#define NICK "tester134" +char *channels[] = {"#channel"}; + + +#define CHECK_NULL() (client.nick != NULL && client.channel != NULL && client.msg != NULL) +IRCC_client client; + +void die(char *msg) { + puts(msg); + IRCC_close(&client); + exit(1); +} + +void recvinfo(void) { + while (1) { + unsigned int irc_status = IRCC_recv(&client); + if (irc_status == IRCC_DISCONNECTED) + die("Disconnected"); + + else if (irc_status == IRCC_CONNECTED) + return; + + //Print + if (CHECK_NULL() && irc_status == IRCC_PRIVMSG) + printf("[%s %s] %s\n", client.channel, client.nick, client.msg); + + else if (CHECK_NULL() && irc_status == IRCC_NICK) + printf("[N] %s is know as %s\n", client.nick, client.msg); + + else if (CHECK_NULL() && irc_status == IRCC_TOPIC) + printf("[T] %s\n", client.msg); + + else if (client.nick != NULL && irc_status == IRCC_JOIN) + printf("[>] %s\n", client.nick); + + else if (client.nick != NULL && irc_status == IRCC_PART) + printf("[<] %s\n", client.nick); + } +} + +int main(void) { + + //512 - size of raw buffer (max irc) + IRCC_init(&client, IRCC_MSG_MAX); + int status = IRCC_connect(&client, HOST, PORT); + if (status == IRCC_ERROR) + die("Conn refused"); + + //Socket timeout + struct timeval tv = {IRCC_PING_TIMEOUT, IRCC_PING_TIMEOUT}; + if (setsockopt(client.socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) + die("setsockopt"); + + //Register + IRCC_register(&client, NICK); + + //Recv motd and join in channel + recvinfo(); + sleep(5); + for (size_t i = 0; i < sizeof(channels) / sizeof(char *); i++) { + if (channels[i] == NULL) + break; + + size_t join_size = strlen("JOIN \r\n") + strlen(channels[i]) + 1; + char *tmp = malloc(join_size); + if (tmp == NULL) + die("malloc retured NULL"); + + snprintf(tmp, join_size, "JOIN %s\r\n", channels[i]); + send(client.socket, tmp, join_size, 0); + free(tmp); + } + + recvinfo(); +} +