--- /dev/null
+/* $Id$ */
+
+#include "ok_bot.h"
+
+#include "ok_util.h"
+#include "ok_news.h"
+
+#include "ircfw.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <strings.h>
+#include <unistd.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <poll.h>
+
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#endif
+
+#define OKUU_VERSION "1.00"
+
+extern char* nntpserver;
+extern char* nntpuser;
+extern char* nntppass;
+extern char* nntppath;
+extern char* nntpcount;
+extern int nntpport;
+
+extern char* ircserver;
+extern char* ircchan;
+extern char* ircpass;
+extern char* ircuser;
+extern char* ircnick;
+extern char* ircreal;
+extern int ircport;
+
+int ok_sock;
+struct sockaddr_in ok_addr;
+
+void ok_close(int sock){
+ close(sock);
+}
+
+void ok_bot_kill(int sig){
+ fprintf(stderr, "Shutdown\n");
+ ircfw_socket_send_cmd(ok_sock, NULL, "QUIT :Shutdown (Signal)");
+ exit(1);
+}
+
+bool ok_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;
+}
+
+char* ok_null(const char* str){
+ return str == NULL ? "(null)" : (char*)str;
+}
+
+int namesort(const struct dirent** a_, const struct dirent** b_){
+ const struct dirent* a = *a_;
+ const struct dirent* b = *b_;
+ return atoi(a->d_name) - atoi(b->d_name);
+}
+
+int nodots(const struct dirent* d){
+ return (strcmp(d->d_name, "..") == 0 || strcmp(d->d_name, ".") == 0) ? 0 : 1;
+}
+
+void ok_bot(void){
+ if((ok_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0){
+ fprintf(stderr, "Socket creation failure\n");
+ return;
+ }
+
+ bzero((char*)&ok_addr, sizeof(ok_addr));
+ ok_addr.sin_family = PF_INET;
+ ok_addr.sin_addr.s_addr = inet_addr(ircserver);
+ ok_addr.sin_port = htons(ircport);
+
+ if(connect(ok_sock, (struct sockaddr*)&ok_addr, sizeof(ok_addr)) < 0){
+ fprintf(stderr, "Connection failure\n");
+ ok_close(ok_sock);
+ return;
+ }
+
+ signal(SIGINT, ok_bot_kill);
+ signal(SIGTERM, ok_bot_kill);
+
+ char* construct = malloc(1025);
+
+ if(ircpass != NULL && strlen(ircpass) > 0){
+ sprintf(construct, "PASS :%s", ircpass);
+ ircfw_socket_send_cmd(ok_sock, NULL, construct);
+ }
+
+ sprintf(construct, "USER %s %s %s :%s", ircuser, ircuser, ircuser, ircreal);
+ ircfw_socket_send_cmd(ok_sock, NULL, construct);
+
+ sprintf(construct, "NICK :%s", ircnick);
+ ircfw_socket_send_cmd(ok_sock, NULL, construct);
+
+ bool is_in = false;
+
+ struct pollfd pollfds[1];
+ pollfds[0].fd = ok_sock;
+ pollfds[0].events = POLLIN | POLLPRI;
+
+ while(1){
+ int r = poll(pollfds, 1, 100);
+ if(!(r > 0 && pollfds[0].revents & POLLIN)){
+ /* 100ms sleep, technically */
+ uint64_t count = 0;
+ FILE* f = fopen(nntpcount, "rb");
+ if(f != NULL){
+ fread(&count, sizeof(count), 1, f);
+ fclose(f);
+ }
+ struct dirent** list;
+ int n = scandir(nntppath, &list, nodots, namesort);
+ if(n >= 0){
+ int i;
+ for(i = 0; i < n; i++){
+ if(count <= atoi(list[i]->d_name)){
+ sprintf(construct, "%s/%s", nntppath, list[i]->d_name);
+ if(ok_news_read(construct) == 0){
+ char* tmp = ok_strcat3("PRIVMSG ", ircchan, " :\x03" "07[USENET] ~ ");
+ char* temp = ok_strcat3(tmp, news_entry.from, "\x03 ");
+ free(tmp);
+ int j;
+ int incr = 0;
+ for(j = 0;; j++){
+ if(news_entry.content[j] == 0 || news_entry.content[j] == '\n'){
+ char* line = malloc(j - incr + 1);
+ line[j - incr] = 0;
+ memcpy(line, news_entry.content + incr, j - incr);
+
+ if(strlen(line) > 0){
+ char* msg = ok_strcat(temp, line);
+ ircfw_socket_send_cmd(ok_sock, NULL, msg);
+ free(msg);
+ }
+
+ free(line);
+ incr = j + 1;
+ if(news_entry.content[j] == 0) break;
+ }
+ }
+ free(temp);
+ }else{
+ fprintf(stderr, "Could not read %s\n", construct);
+ }
+ }
+ free(list[i]);
+ }
+ count = atoi(list[i - 1]->d_name) + 1;
+ free(list);
+ }
+ f = fopen(nntpcount, "wb");
+ if(f != NULL){
+ fwrite(&count, sizeof(count), 1, f);
+ fclose(f);
+ }
+ continue;
+ }
+ int st = ircfw_socket_read_cmd(ok_sock);
+ if(st != 0){
+ fprintf(stderr, "Bad response\n");
+ return;
+ }
+ if(strlen(ircfw_message.command) == 3 && ok_is_number(ircfw_message.command)){
+ int res = atoi(ircfw_message.command);
+ if(!is_in && 400 <= res && res <= 599){
+ fprintf(stderr, "Bad response\n");
+ return;
+ }else if(400 <= res && res <= 599){
+ fprintf(stderr, "Ignored error: %d\n", res);
+ continue;
+ }
+ if(res == 376){
+ is_in = true;
+ fprintf(stderr, "Login successful\n");
+ sprintf(construct, "JOIN :%s", ircchan);
+ ircfw_socket_send_cmd(ok_sock, NULL, construct);
+ }
+ }else{
+ if(strcasecmp(ircfw_message.command, "PING") == 0){
+ fprintf(stderr, "Ping request\n");
+ sprintf(construct, "PONG :%s", ok_null(ircfw_message.prefix));
+ ircfw_socket_send_cmd(ok_sock, NULL, construct);
+ }else if(strcasecmp(ircfw_message.command, "PRIVMSG") == 0){
+ char* prefix = ircfw_message.prefix;
+ char** params = ircfw_message.params;
+
+ int len = 0;
+ if(params != NULL) {
+ int i;
+ for(i = 0; params[i] != NULL; i++);
+ len = i;
+ }
+ if(prefix != NULL && len == 2) {
+ char* sentin = params[0];
+ char* msg = params[1];
+ char* nick = ok_strdup(prefix);
+ int i;
+ for(i = 0; nick[i] != 0; i++) {
+ if(nick[i] == '!') {
+ nick[i] = 0;
+ break;
+ }
+ }
+ if(msg[0] == 1 && msg[strlen(msg) - 1] == 1){
+ /* CTCP */
+ if(strcasecmp(msg, "\x01VERSION\x01") == 0){
+ sprintf(construct, "NOTICE %s :\x01VERSION Okuu %s / IRC Frameworks %s: http://nishi.boats/okuu\x01", nick, OKUU_VERSION, IRCFW_VERSION);
+ ircfw_socket_send_cmd(ok_sock, NULL, construct);
+ }
+ }else if(sentin[0] == '#'){
+ /* This was sent in channel */
+ }
+
+ free(nick);
+ }
+ }else if(strcasecmp(ircfw_message.command, "ERROR") == 0){
+ if(ircfw_message.params != NULL){
+ int i;
+ for(i = 0; ircfw_message.params[i] != NULL; i++) fprintf(stderr, "ERROR: %s\n", ircfw_message.params[i]);
+ }
+ }
+ }
+ }
+
+ ircfw_socket_send_cmd(ok_sock, NULL, "QUIT :Shutdown");
+
+ free(construct);
+}
--- /dev/null
+/* $Id$ */
+
+#define OK_NEWS_SRC
+#include "ok_news.h"
+
+#include "ok_util.h"
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/stat.h>
+
+struct news_entry news_entry;
+
+void ok_news_init(void){
+ news_entry.from = NULL;
+ news_entry.content = NULL;
+}
+
+int ok_news_read(const char* path){
+
+ if(news_entry.from != NULL){
+ free(news_entry.from);
+ news_entry.from = NULL;
+ }
+ if(news_entry.content != NULL){
+ free(news_entry.content);
+ news_entry.content = NULL;
+ }
+
+ struct stat s;
+ if(stat(path, &s) == 0){
+ char* boundary = NULL;
+ char* buffer = malloc(s.st_size + 1);
+ buffer[s.st_size] = 0;
+ FILE* f = fopen(path, "r");
+ fread(buffer, s.st_size, 1, f);
+
+ uint64_t i;
+ bool newline = false;
+ int incr = 0;
+ char* l = malloc(1);
+ l[0] = 0;
+ bool header = true;
+ bool ignore = false;
+ bool bheader = false;
+ for(i = 0; i < s.st_size; i++){
+ if(buffer[i] == '\r'){
+ if(buffer[i + 1] == '\n'){
+ /* newline */
+ i++;
+ if(!header){
+ char* line = malloc(i - 1 - incr + 1);
+ line[i - 1 - incr] = 0;
+ memcpy(line, buffer + incr, i - 1 - incr);
+
+ if(strcmp(line, ".") == 0){
+ free(line);
+ break;
+ }else{
+ char* ln = line;
+ if(line[0] == '.'){
+ ln++;
+ }
+
+ if(news_entry.content == NULL){
+ news_entry.content = malloc(1);
+ news_entry.content[0] = 0;
+ }
+
+ if(boundary != NULL && strcmp(ln, boundary) == 0){
+ bheader = true;
+ ignore = true;
+ }else if(boundary != NULL && bheader && strlen(ln) == 0){
+ bheader = false;
+ free(line);
+ incr = i + 1;
+ newline = true;
+ continue;
+ }else if(boundary != NULL && bheader){
+ int j;
+ for(j = 0; j < strlen(ln); j++){
+ if(ln[j] == ':'){
+ ln[j] = 0;
+ if(strcasecmp(ln, "Content-Type") == 0){
+ ignore = false;
+ j++;
+ for(; ln[j] != 0 && (ln[j] == ' ' || ln[j] == '\t'); j++);
+ if(ln[j] != 0){
+ char* v = ln + j;
+ int k;
+ for(k = 0; v[k] != 0; k++){
+ if(v[k] == ';'){
+ v[k] = 0;
+ break;
+ }
+ }
+ if(strcasecmp(v, "text/plain") == 0){
+ }else{
+ ignore = true;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ if(!ignore && !bheader){
+ char* tmp = news_entry.content;
+ news_entry.content = ok_strcat3(tmp, ln, "\n");
+ free(tmp);
+ }
+ }
+
+ free(line);
+ }else if(newline){
+ header = false;
+ }else{
+ char* line = malloc(i - 1 - incr + 1);
+ line[i - 1 - incr] = 0;
+ memcpy(line, buffer + incr, i - 1 - incr);
+
+ char* last = ok_strdup(l);
+ char* tmp = l;
+ l = ok_strcat(tmp, line);
+ free(tmp);
+ bool al = false;
+ if(('a' <= line[0] && line[0] <= 'z') || ('A' <= line[0] && line[0] <= 'Z')){
+ free(l);
+ l = ok_strdup(line);
+ al = true;
+ }
+ if(al){
+ char* ln = ok_strdup(l);
+ int j;
+ for(j = 0; ln[j] != 0; j++){
+ if(ln[j] == ':'){
+ char* key = ln;
+ char* value = "";
+ ln[j] = 0;
+ j++;
+ for(; ln[j] != 0 && (ln[j] == '\t' || ln[j] == ' '); j++);
+ if(ln[j] != 0) value = ln + j;
+ if(strcasecmp(key, "From") == 0){
+ if(news_entry.from != NULL) free(news_entry.from);
+ news_entry.from = ok_strdup(value);
+ }else if(strcasecmp(key, "Content-Type") == 0){
+ int k = 0;
+ int incr2 = 0;
+ for(k = 0; k <= strlen(value); k++){
+ if(value[k] == ';' || value[k] == 0){
+ char* attr = malloc(k - incr2 + 1);
+ attr[k - incr2] = 0;
+ memcpy(attr, value + incr2, k - incr2);
+
+ int in;
+ for(in = 0; attr[in] != 0; in++){
+ if(attr[in] == '='){
+ attr[in] = 0;
+
+ if(strcasecmp(attr, "boundary") == 0){
+ boundary = ok_strcat("--", attr + in + 1 + 1);
+ boundary[strlen(attr + in + 1 + 1) - 1 + 2] = 0;
+ ignore = true;
+ }
+
+ break;
+ }
+ }
+
+ free(attr);
+ k++;
+ for(; value[k] != 0 && (value[k] == ' ' || value[k] == '\t'); k++);
+ incr2 = k;
+ }
+ }
+ }
+ }
+ }
+ free(ln);
+ }
+ free(last);
+ free(line);
+ }
+ incr = i + 1;
+ newline = true;
+ }else{
+ newline = false;
+ }
+ }else{
+ newline = false;
+ }
+ }
+ free(l);
+
+ free(buffer);
+ if(boundary != NULL) free(boundary);
+ return 0;
+ }else{
+ return 1;
+ }
+}