diff --git a/Bot/Makefile b/Bot/Makefile index e4cf52e..b12af2f 100644 --- a/Bot/Makefile +++ b/Bot/Makefile @@ -5,7 +5,7 @@ include $(PWD)/Platform/$(PLATFORM).mk .PHONY: all clean .SUFFIXES: .c .o -OBJS = main.o util.o bot.o +OBJS = main.o util.o bot.o ircfw.o all: ircarc$(EXEC) diff --git a/Bot/bot.c b/Bot/bot.c index 2d506dc..86b60ea 100644 --- a/Bot/bot.c +++ b/Bot/bot.c @@ -2,16 +2,23 @@ #include "ia_bot.h" +#include "ia_util.h" + +#include "ircfw.h" + +#include #include +#include #include #include +#include #include #include #include int ia_sock; -struct sockaddr_in is_addr; +struct sockaddr_in ia_addr; bool ia_do_log = false; @@ -21,32 +28,131 @@ void ia_log(const char* txt) { } extern char* host; +extern char* realname; +extern char* nickname; +extern char* username; +extern char* password; +extern char* channels[]; extern int port; +void ia_close(int sock) { close(sock); } + +const char* ia_null(const char* str) { + if(str == NULL) return "(null)"; + return str; +} + +bool ia_is_number(const char* str) { + int i; + for(i = 0; str[i] != 0; i++) { + if(!('0' <= str[i] && str[i] <= '9')) return false; + } + return true; +} + +bool loop = true; + +void ia_bot_kill(int sig) { + ia_log("Shutdown"); + ircfw_socket_send_cmd(ia_sock, NULL, "QUIT :Shutdown (Signal)"); + exit(1); +} + void ia_bot_loop(void) { if((ia_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) { ia_log("Socket creation failure"); return; } + loop = true; + int yes = 1; if(setsockopt(ia_sock, IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(yes)) < 0) { ia_log("setsockopt failure"); + ia_close(ia_sock); return; } - bzero((char*)&is_addr, sizeof(is_addr)); - is_addr.sin_family = PF_INET; - is_addr.sin_addr.s_addr = inet_addr(host); - is_addr.sin_port = htons(port); + bzero((char*)&ia_addr, sizeof(ia_addr)); + ia_addr.sin_family = PF_INET; + ia_addr.sin_addr.s_addr = inet_addr(host); + ia_addr.sin_port = htons(port); if(connect(ia_sock, (struct sockaddr*)&ia_addr, sizeof(ia_addr)) < 0) { ia_log("Connection failure"); - close(ia_sock); - return 1; + ia_close(ia_sock); + return; } - while(1) { + signal(SIGINT, ia_bot_kill); + signal(SIGTERM, ia_bot_kill); + + char* construct = malloc(1025); + + if(password != NULL && strlen(password) > 0) { + sprintf(construct, "PASS :%s", password); + ircfw_socket_send_cmd(ia_sock, NULL, construct); } + + sprintf(construct, "USER %s %s %s :%s", username, username, username, realname); + ircfw_socket_send_cmd(ia_sock, NULL, construct); + + sprintf(construct, "NICK :%s", nickname); + ircfw_socket_send_cmd(ia_sock, NULL, construct); + + bool is_in = false; + + while(1) { + int st = ircfw_socket_read_cmd(ia_sock); + if(st != 0) { + ia_log("Bad response"); + return; + } + if(strlen(ircfw_message.command) == 3 && ia_is_number(ircfw_message.command)) { + int res = atoi(ircfw_message.command); + if(!is_in && 400 <= res && res <= 599) { + ia_log("Bad response"); + return; + } else if(400 <= res && res <= 599) { + sprintf(construct, "Ignored error: %d", res); + ia_log(construct); + continue; + } + if(res == 376) { + is_in = true; + ia_log("Login successful"); + int i; + for(i = 0; channels[i] != NULL; i++) { + sprintf(construct, "JOIN :%s", channels[i]); + ircfw_socket_send_cmd(ia_sock, NULL, construct); + } + } + } else { + if(strcasecmp(ircfw_message.command, "PING") == 0) { + ia_log("Ping request"); + sprintf(construct, "PONG :%s", ia_null(ircfw_message.prefix)); + ircfw_socket_send_cmd(ia_sock, NULL, construct); + } else if(strcasecmp(ircfw_message.command, "PRIVMSG") == 0) { + char* prefix = ircfw_message.prefix; + char** params = ircfw_message.params; + if(prefix != NULL && params != NULL) { + char* nick = ia_strdup(prefix); + int i; + for(i = 0; nick[i] != 0; i++) { + if(nick[i] == '!') { + nick[i] = 0; + break; + } + } + + free(nick); + } + } + } + } + + ircfw_socket_send_cmd(ia_sock, NULL, "QUIT :Shutdown"); + + free(construct); } diff --git a/Bot/ia_bot.h b/Bot/ia_bot.h index f78d89d..c0e22a2 100644 --- a/Bot/ia_bot.h +++ b/Bot/ia_bot.h @@ -3,6 +3,8 @@ #ifndef __IA_BOT_H__ #define __IA_BOT_H__ +void ia_log(const char* txt); +void is_close(int sock); void ia_bot_loop(void); #endif diff --git a/Bot/ircfw.c b/Bot/ircfw.c new file mode 100644 index 0000000..967fbec --- /dev/null +++ b/Bot/ircfw.c @@ -0,0 +1,165 @@ +/* $Id$ */ + +#define IRCFW_SRC +#include "ircfw.h" + +#include +#include +#include + +#include +#include +#include + +struct ircfw_message ircfw_message; + +char* ircfw_strcat(const char* a, const char* b) { + char* str = malloc(strlen(a) + strlen(b) + 1); + memcpy(str, a, strlen(a)); + memcpy(str + strlen(a), b, strlen(b)); + str[strlen(a) + strlen(b)] = 0; + return str; +} + +char* ircfw_strcat3(const char* a, const char* b, const char* c) { + char* tmp = ircfw_strcat(a, b); + char* str = ircfw_strcat(tmp, c); + free(tmp); + return str; +} + +char* ircfw_strdup(const char* str) { return ircfw_strcat(str, ""); } + +void ircfw_init(void) { + ircfw_message.prefix = NULL; + ircfw_message.params = NULL; + ircfw_message.command = NULL; +} + +void ircfw_parse_params(const char* str) { + ircfw_message.params = malloc(sizeof(*ircfw_message.params)); + ircfw_message.params[0] = NULL; + int i; + int incr = 0; + bool until_end = false; + char* dup = ircfw_strdup(str); + for(i = 0;; i++) { + if(dup[i] == 0 || (!until_end && dup[i] == ' ')) { + char oldc = dup[i]; + dup[i] = 0; + + char* param = ircfw_strdup(dup + incr); + + char** old_params = ircfw_message.params; + int j; + for(j = 0; old_params[j] != NULL; j++) + ; + ircfw_message.params = malloc(sizeof(*ircfw_message.params) * (2 + j)); + for(j = 0; old_params[j] != NULL; j++) { + ircfw_message.params[j] = old_params[j]; + } + ircfw_message.params[j] = param; + ircfw_message.params[j + 1] = NULL; + free(old_params); + + incr = i + 1; + if(oldc == 0) break; + } else if(dup[i] == ':' && !until_end) { + until_end = true; + incr = i + 1; + } + } + free(dup); +} + +int ircfw_socket_send_cmd(int sock, const char* name, const char* cmd) { + char* str = ircfw_strcat(cmd, "\r\n"); + if(name != NULL) { + char* old = str; + char* tmp = ircfw_strcat3(":", name, " "); + str = ircfw_strcat(tmp, old); + free(old); + free(tmp); + } + int st = send(sock, str, strlen(str), 0); + free(str); + return st < 0 ? 1 : 0; +} + +int ircfw_socket_read_cmd(int sock) { + char c[2]; + c[1] = 0; + char* str = malloc(1); + str[0] = 0; + bool err = false; + bool end = false; + while(1) { + int s = recv(sock, c, 1, 0); + if(s <= 0) { + err = true; + break; + } + if(c[0] == '\n') { + end = true; + break; + } else if(c[0] != '\r') { + char* tmp = str; + str = ircfw_strcat(tmp, c); + free(tmp); + } + } + if(ircfw_message.prefix != NULL) free(ircfw_message.prefix); + if(ircfw_message.params != NULL) { + int i; + for(i = 0; ircfw_message.params[i] != NULL; i++) { + free(ircfw_message.params[i]); + } + free(ircfw_message.params); + } + if(ircfw_message.command != NULL) free(ircfw_message.command); + ircfw_message.prefix = NULL; + ircfw_message.params = NULL; + ircfw_message.command = NULL; + + if(str[0] == ':') { + int i; + for(i = 0; str[i] != 0; i++) { + if(str[i] == ' ') { + str[i] = 0; + ircfw_message.prefix = ircfw_strdup(str + 1); + i++; + int start = i; + for(;; i++) { + if(str[i] == ' ' || str[i] == 0) { + char oldc = str[i]; + str[i] = 0; + ircfw_message.command = ircfw_strdup(str + start); + if(oldc != 0) { + i++; + ircfw_parse_params(str + i); + } + break; + } + } + break; + } + } + } else { + int i; + for(i = 0; str[i] != 0; i++) { + if(str[i] == ' ' || str[i] == 0) { + char oldc = str[i]; + str[i] = 0; + ircfw_message.command = ircfw_strdup(str); + if(oldc != 0) { + i++; + ircfw_parse_params(str + i); + } + break; + } + } + } + + free(str); + return err ? 1 : 0; +} diff --git a/Bot/ircfw.h b/Bot/ircfw.h new file mode 100644 index 0000000..2d463eb --- /dev/null +++ b/Bot/ircfw.h @@ -0,0 +1,20 @@ +/* $Id$ */ + +#ifndef __IRCFW_H__ +#define __IRCFW_H__ + +struct ircfw_message { + char* prefix; + char* command; + char** params; +}; + +void ircfw_init(void); +int ircfw_socket_send_cmd(int sock, const char* name, const char* cmd); +int ircfw_socket_read_cmd(int sock); + +#ifndef IRCFW_SRC +extern struct ircfw_message ircfw_message; +#endif + +#endif diff --git a/Bot/main.c b/Bot/main.c index 371afd2..768ee81 100644 --- a/Bot/main.c +++ b/Bot/main.c @@ -14,9 +14,13 @@ extern bool ia_do_log; char* host = NULL; int port = 0; +char* nickname = NULL; char* username = NULL; +char* realname = NULL; char* password = NULL; char* admin = NULL; +char* channels[128]; +int chanincr; int main(int argc, char** argv) { const char* fn = "archiver.ini"; @@ -51,6 +55,8 @@ int main(int argc, char** argv) { int incr = 0; + channels[0] = NULL; + for(i = 0;; i++) { if(buf[i] == 0 || buf[i] == '\n') { char oldc = buf[i]; @@ -72,12 +78,21 @@ int main(int argc, char** argv) { } else if(strcmp(key, "username") == 0) { if(username != NULL) free(username); username = ia_strdup(value); + } else if(strcmp(key, "nickname") == 0) { + if(nickname != NULL) free(nickname); + nickname = ia_strdup(value); } else if(strcmp(key, "password") == 0) { if(password != NULL) free(password); password = ia_strdup(value); } else if(strcmp(key, "admin") == 0) { if(admin != NULL) free(admin); admin = ia_strdup(value); + } else if(strcmp(key, "realname") == 0) { + if(realname != NULL) free(realname); + realname = ia_strdup(value); + } else if(strcmp(key, "channel") == 0) { + channels[chanincr++] = ia_strdup(value); + channels[chanincr] = NULL; } break; @@ -101,6 +116,10 @@ int main(int argc, char** argv) { fprintf(stderr, "Specify username\n"); st = 1; } + if(nickname == NULL) { + fprintf(stderr, "Specify nickname\n"); + st = 1; + } if(password == NULL) { fprintf(stderr, "Specify password\n"); st = 1; @@ -109,6 +128,10 @@ int main(int argc, char** argv) { fprintf(stderr, "Specify admin\n"); st = 1; } + if(realname == NULL) { + fprintf(stderr, "Specify realname\n"); + st = 1; + } if(st == 1) return st; printf("Bot spawning a daemon\n"); @@ -124,7 +147,12 @@ int main(int argc, char** argv) { } if(host != NULL) free(host); + if(realname != NULL) free(realname); if(username != NULL) free(username); + if(nickname != NULL) free(nickname); if(password != NULL) free(password); if(admin != NULL) free(admin); + for(i = 0; channels[i] != NULL; i++) { + free(channels[i]); + } }