Yakman
05-02-2008, 03:43 PM
IRC Bot by yakman
Joins one server and one channel
has commands which start with a COMMAND_PREFIX, default is '!'
say [message] - repeats [message] back to the channel
fact - parses a website and says a fact
slogan - parses a website and says an advertising slogan
quote - parses a website and says a quote
insult [name] - parses a website and insults [name]
Compiles on unix and windows,
tested with GCC on unix and MinGW on windows
<yakman> !fact
<slurry_> Nurses and doctors wash their hands between patients less than one-third of the time.
<yakman> !quote
<slurry_> "People demand freedom of speech to make up for the freedom of thought which they avoid." - Soren Aabye Kierkegaard (1813-1855)
<yakman> !say hello world
<slurry_> hello world
<yakman> !slogan
<slurry_> Refreshes the Anxiety Other Beers Cannot Reach
<yakman> !insult Cowie`
<slurry_> Cowie`: I'd like to see things from your point of view but I can't seem to get my head that far up my ass.
i was wondering, is there a way you can list which libraries on windows to link, so you can write
gcc ircbot.c
instead of
gcc -L"lib\libws2_32.a" ircbot.c
heres the bot
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
IRC Bot by yakman
Joins one server and one channel
has commands which start with a COMMAND_PREFIX, default is '!'
say [message] - repeats [message] back to the channel
fact - parses a website and says a fact
slogan - parses a website and says an advertising slogan
quote - parses a website and says a quote
insult [name] - parses a website and insults [name]
Compiles on unix and windows,
tested with GCC on unix and MinGW on windows
To compile on unix
gcc -std=c99 -o ircbot ircbot.c
Compiling on windows varies, one thing you have to do is to link the the ws2_32 library
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//unix specific stuff
#ifdef __unix__
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#else
//windows specific stuff
#ifdef __WIN32__
#include <winsock2.h>
#else
#error "Only compiles on unix and windows"
#endif
#endif
//stuff to change
#define IRC_SERVER "irc.dairc.net"
#define IRC_PORT 6667
#define NICK "slurry_"
#define CHANNEL "#bots"
//which hostname can !quit the bot
#define MASTER_HOST "commandante.che.guevara"
//need the master_host for all commands?
//#define NEED_MASTER 1
#define COMMAND_PREFIX '!'
#define BUFFER_SIZE 64
//thesurrealist.co.uk/slogan.cgi
//www.randomfunfacts.com
//www.quotability.com
//www.randominsults.net
//no IRC stuff below yet, only the website parsing
char* downloadHTTP(char* hostName, char* page) {
struct hostent* host = gethostbyname(hostName);
if(host == NULL) {
#ifdef __unix__
herror("Error resolving host name");
#else
printf("Error resolving host name. errorcode: %i\n", WSAGetLastError());
#endif
return NULL;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr = ((struct in_addr*)host->h_addr)->s_addr;
int sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < 0) {
perror("failed to open socket");
return NULL;
}
if(connect(sock_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("failed to connect");
return NULL;
}
char* one = "GET /";
char* three = " HTTP/1.1\r\nHost: ";
char* five = "\r\nUser-Agent: Mozilla/5.0 (Linux; X11; UTF-8)\r\nConnection: Close\r\n\r\n";
int httplen = strlen(one) + strlen(page) + strlen(three) + strlen(hostName) + strlen(five);
char* http = (char*)malloc(httplen + 1);
*http = '\0';
strcat(http, one);
strcat(http, page);
strcat(http, three);
strcat(http, hostName);
strcat(http, five);
send(sock_fd, http, httplen, 0);
free(http);
int pos = 0;
int len = BUFFER_SIZE;
char* buffer = (char*)malloc(len);
while(1) {
char buf[BUFFER_SIZE];
int reclen = recv(sock_fd, buf, BUFFER_SIZE, 0);
if(reclen <= 0)
break;
if(reclen + pos > len-1) {
len = (len * 3 / 2) + 1;
buffer = (char*)realloc(buffer, len);
}
memcpy(buffer + pos, buf, reclen);
pos += reclen;
}
buffer[pos] = '\0';
#ifdef __unix__
close(sock_fd);
#else
closesocket(sock_fd);
#endif
char* headerEnd = strstr(buffer, "\r\n\r\n");
char* payloadEnd = headerEnd + strlen(headerEnd);
char* result = (char*)malloc(payloadEnd - headerEnd + 1);
memcpy(result, headerEnd, payloadEnd - headerEnd);
result[payloadEnd - headerEnd] = '\0';
free(buffer);
buffer = NULL;
return result;
}
char* getFact(void) {
char* page = downloadHTTP("www.randomfunfacts.com", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
char* getSlogan(void) {
char* page = downloadHTTP("thesurrealist.co.uk", "slogan.cgi");
if(page == NULL)
return NULL;
char* first = strstr(page, "class=\"shirtaw\">");
if(first == NULL)
return NULL;
first += 16;
char* last = strstr(first, "</a>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
char* getInsult(void) {
char* page = downloadHTTP("www.randominsults.net", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
char* getQuote(void) {
char* page = downloadHTTP("www.quotability.com", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
//IRC processing happens below
int connectToIRC(char* server, int port) {
printf("Connecting to %s:%i\n", server, port);
struct hostent* host = gethostbyname(server);
if(host == NULL) {
#ifdef __unix__
herror("Error resolving host name");
#else
printf("Error resolving host name. errorcode: %i\n", WSAGetLastError());
#endif
return -1;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = ((struct in_addr*)host->h_addr)->s_addr;
int sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < 0) {
perror("failed to open socket");
return -1;
}
if(connect(sock_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("failed to connect");
return -1;
}
return sock_fd;
}
struct BufferedSocketReader {
int sock_fd;
int closed; //is set if the file is closed AND the buffer is empty
char* buffer; //note buffer isnt null-terminated
int pos;
int len;
};
void sendData(struct BufferedSocketReader* reader, char* line) {
//makes a copy of the line with a linebreak added
int lbLen = strlen(line) + 2;
char* lineLB = (char*)malloc(lbLen);
memcpy(lineLB, line, lbLen - 1);
lineLB[lbLen - 2] = '\n';
lineLB[lbLen - 1] = '\0';
printf("=> %s", lineLB);
send(reader->sock_fd, lineLB, strlen(lineLB), 0); //TODO check return value and resend if not everything is sent
free(lineLB);
}
void closeSocketReader(struct BufferedSocketReader* reader) {
#ifdef __unix__
close(reader->sock_fd);
#else
closesocket(reader->sock_fd);
#endif
reader->sock_fd = -1;
}
void fillSocketBuffer(struct BufferedSocketReader* reader) {
if(reader->sock_fd == -1) {
reader->closed = 1;
return;
}
char buf[BUFFER_SIZE];
int recLen = recv(reader->sock_fd, buf, BUFFER_SIZE, 0);
if(recLen < 0) {
closeSocketReader(reader);
}
int newLen = (reader->len - reader->pos) + recLen; //new length is the length already in the buffer, and length of just recved
char* newBuf = (char*)malloc(newLen);
memset(newBuf, '\0', newLen);
memcpy(newBuf, reader->buffer + reader->pos, reader->len - reader->pos); //copy the old buffer into new
memcpy(newBuf + reader->len - reader->pos, buf, recLen); //copy receved buffer into new buffer
free(reader->buffer);
reader->buffer = newBuf;
reader->len = newLen;
reader->pos = 0;
}
char* readNextLine(struct BufferedSocketReader* reader) {
//bug, if we reach the EOF and there is no line break, the data in the buffer will be discarded
//not likely to happen on irc protocol though
while(!reader->closed) {
for(int i = reader->pos; i < reader->len-1; i++) { //-1 to allow for the check for double linebreak
if(reader->buffer[i] == '\r' || reader->buffer[i] == '\n') {
int len = i - reader->pos;
char* result = (char*)malloc(len + 1);
memcpy(result, reader->buffer + reader->pos, len);
result[len] = '\0';
reader->pos = i + 1; //skip the char you just read
//sometimes line breaks come in twos
if(reader->buffer[reader->pos] == '\r' || reader->buffer[reader->pos] == '\n')
reader->pos++;
return result;
}
}
//didnt find \r\n, need to read more data into the buffer
fillSocketBuffer(reader);
}
//socket is closed and buffer is empty
return NULL;
}
void handleCommand(struct BufferedSocketReader* reader, char* nick, char* user, char* host, char* cmd, char* chan, char* text) {
#ifdef NEED_MASTER
if(strstr(host, MASTER_HOST) == NULL)
return;
#endif
if(strstr(chan, CHANNEL) == NULL) //someone sent private message, ie. not from channel
return;
if(strstr(text, "quit") == text && strstr(host, MASTER_HOST) != NULL) {
sendData(reader, "QUIT :leaving");
printf("Quitting\n");
closeSocketReader(reader);
}else
if(strstr(text, "say") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(text + 4) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, text + 4);
sendData(reader, sayline);
free(sayline);
}else
if(strstr(text, "fact") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* fact = getFact();
if(fact == NULL)
fact = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(fact) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, fact);
sendData(reader, sayline);
free(fact);
free(sayline);
}else
if(strstr(text, "slogan") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* slogan = getSlogan();
if(slogan == NULL)
slogan = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(slogan) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, slogan);
sendData(reader, sayline);
free(slogan);
free(sayline);
}else
if(strstr(text, "insult") == text) {
char* target = NULL;
if(strchr(text, ' ') != NULL) {
char* t = strchr(text, ' ');
*t = '\0';
target = t + 1;
//if there is a trailing ' ' we have to remove
if(strchr(target, ' ') != NULL) {
char* t = strchr(target, ' ');
*t = '\0';
}
}
char* cmd = "PRIVMSG ";
char* colon = " :";
char* insult = getInsult();
if(insult == NULL)
insult = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) +
(target == NULL ? 0 : strlen(target) + 2) + strlen(insult) + 1;
//add 2 if there is a target, for the colon and space
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
if(target != NULL) {
strcat(sayline, target);
strcat(sayline, ": ");
}
strcat(sayline, insult);
sendData(reader, sayline);
free(insult);
free(sayline);
}else
if(strstr(text, "quote") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* quote = getQuote();
if(quote == NULL)
quote = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(quote) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, quote);
sendData(reader, sayline);
free(quote);
free(sayline);
}
}
void handleLine(struct BufferedSocketReader* reader, char* line) {
printf("<= %s\n", line);
char* lineTok = (char*)malloc(strlen(line) + 1);
strcpy(lineTok, line);
if(strstr(line, "PING") == line) {
line[1] = 'O';
sendData(reader, line);
printf("Replied to ping\n");
free(lineTok);
return;
}
//has the ":nick!username@host" format
//also works for other stuff
if(*lineTok != ':') {
free(lineTok);
return;
}
char* nickuserhost = strtok(lineTok + 1, " ");
char* cmd = strtok(NULL, " ");
char* chan = strtok(NULL, " ");
char* text = strtok(NULL, "\0");
char* nick = NULL;
char* user = NULL;
char* host = NULL;
//split nick, user and host
if(nickuserhost != NULL && strchr(nickuserhost, '!') != NULL && strchr(nickuserhost, '@') != NULL) {
nick = strtok(nickuserhost, "!");
user = strtok(NULL, "@");
host = strtok(NULL, "\0");
}
if(text != NULL && *text == ':') //remove the first ':' on chat
text++;
//below is the using of the strings
if(strcmp(cmd, "PRIVMSG") == 0 && *text == COMMAND_PREFIX) { //someone has spoken
handleCommand(reader, nick, user, host, cmd, chan, text + 1);
}
if(strcmp(cmd, "KICK") == 0 && strstr(text, NICK) == text) {
char* header = "JOIN "; //send "JOIN #channel"
char* chanLine = (char*)malloc(strlen(header) + strlen(CHANNEL) + 1);
*chanLine = '\0';
strcat(chanLine, header);
strcat(chanLine, CHANNEL);
sendData(reader, chanLine);
free(chanLine);
}
free(lineTok);
}
int main(int argc, char** argv) {
#ifdef __WIN32__
WSADATA wsaData;
if(WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
printf("Error starting winsock\n");
return EXIT_FAILURE;
}
#endif
struct BufferedSocketReader reader;
reader.sock_fd = connectToIRC(IRC_SERVER, IRC_PORT);
reader.closed = 0;
reader.pos = 0;
sendData(&reader, "USER user 8 * :real");
char* header = "NICK "; //send "NICK mynick"
char* nickLine = (char*)malloc(strlen(header) + strlen(NICK) + 1);
*nickLine = '\0';
strcat(nickLine, header);
strcat(nickLine, NICK);
sendData(&reader, nickLine);
free(nickLine);
header = "JOIN "; //send "JOIN #channel"
char* chanLine = (char*)malloc(strlen(header) + strlen(CHANNEL) + 1);
*chanLine = '\0';
strcat(chanLine, header);
strcat(chanLine, CHANNEL);
sendData(&reader, chanLine);
free(chanLine);
char buf[BUFFER_SIZE];
reader.len = recv(reader.sock_fd, buf, BUFFER_SIZE, 0);
reader.buffer = (char*)malloc(reader.len); //initalise buffer
memcpy(reader.buffer, buf, reader.len);
while(!reader.closed) {
char* line = readNextLine(&reader);
if(line == NULL)
continue;
handleLine(&reader, line);
free(line);
}
printf("Socket closed or buffer empty\nExiting...\n");
#ifdef __WIN32__
WSACleanup();
system("PAUSE");
#endif
return EXIT_SUCCESS;
}
Joins one server and one channel
has commands which start with a COMMAND_PREFIX, default is '!'
say [message] - repeats [message] back to the channel
fact - parses a website and says a fact
slogan - parses a website and says an advertising slogan
quote - parses a website and says a quote
insult [name] - parses a website and insults [name]
Compiles on unix and windows,
tested with GCC on unix and MinGW on windows
<yakman> !fact
<slurry_> Nurses and doctors wash their hands between patients less than one-third of the time.
<yakman> !quote
<slurry_> "People demand freedom of speech to make up for the freedom of thought which they avoid." - Soren Aabye Kierkegaard (1813-1855)
<yakman> !say hello world
<slurry_> hello world
<yakman> !slogan
<slurry_> Refreshes the Anxiety Other Beers Cannot Reach
<yakman> !insult Cowie`
<slurry_> Cowie`: I'd like to see things from your point of view but I can't seem to get my head that far up my ass.
i was wondering, is there a way you can list which libraries on windows to link, so you can write
gcc ircbot.c
instead of
gcc -L"lib\libws2_32.a" ircbot.c
heres the bot
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
IRC Bot by yakman
Joins one server and one channel
has commands which start with a COMMAND_PREFIX, default is '!'
say [message] - repeats [message] back to the channel
fact - parses a website and says a fact
slogan - parses a website and says an advertising slogan
quote - parses a website and says a quote
insult [name] - parses a website and insults [name]
Compiles on unix and windows,
tested with GCC on unix and MinGW on windows
To compile on unix
gcc -std=c99 -o ircbot ircbot.c
Compiling on windows varies, one thing you have to do is to link the the ws2_32 library
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//unix specific stuff
#ifdef __unix__
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#else
//windows specific stuff
#ifdef __WIN32__
#include <winsock2.h>
#else
#error "Only compiles on unix and windows"
#endif
#endif
//stuff to change
#define IRC_SERVER "irc.dairc.net"
#define IRC_PORT 6667
#define NICK "slurry_"
#define CHANNEL "#bots"
//which hostname can !quit the bot
#define MASTER_HOST "commandante.che.guevara"
//need the master_host for all commands?
//#define NEED_MASTER 1
#define COMMAND_PREFIX '!'
#define BUFFER_SIZE 64
//thesurrealist.co.uk/slogan.cgi
//www.randomfunfacts.com
//www.quotability.com
//www.randominsults.net
//no IRC stuff below yet, only the website parsing
char* downloadHTTP(char* hostName, char* page) {
struct hostent* host = gethostbyname(hostName);
if(host == NULL) {
#ifdef __unix__
herror("Error resolving host name");
#else
printf("Error resolving host name. errorcode: %i\n", WSAGetLastError());
#endif
return NULL;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr = ((struct in_addr*)host->h_addr)->s_addr;
int sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < 0) {
perror("failed to open socket");
return NULL;
}
if(connect(sock_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("failed to connect");
return NULL;
}
char* one = "GET /";
char* three = " HTTP/1.1\r\nHost: ";
char* five = "\r\nUser-Agent: Mozilla/5.0 (Linux; X11; UTF-8)\r\nConnection: Close\r\n\r\n";
int httplen = strlen(one) + strlen(page) + strlen(three) + strlen(hostName) + strlen(five);
char* http = (char*)malloc(httplen + 1);
*http = '\0';
strcat(http, one);
strcat(http, page);
strcat(http, three);
strcat(http, hostName);
strcat(http, five);
send(sock_fd, http, httplen, 0);
free(http);
int pos = 0;
int len = BUFFER_SIZE;
char* buffer = (char*)malloc(len);
while(1) {
char buf[BUFFER_SIZE];
int reclen = recv(sock_fd, buf, BUFFER_SIZE, 0);
if(reclen <= 0)
break;
if(reclen + pos > len-1) {
len = (len * 3 / 2) + 1;
buffer = (char*)realloc(buffer, len);
}
memcpy(buffer + pos, buf, reclen);
pos += reclen;
}
buffer[pos] = '\0';
#ifdef __unix__
close(sock_fd);
#else
closesocket(sock_fd);
#endif
char* headerEnd = strstr(buffer, "\r\n\r\n");
char* payloadEnd = headerEnd + strlen(headerEnd);
char* result = (char*)malloc(payloadEnd - headerEnd + 1);
memcpy(result, headerEnd, payloadEnd - headerEnd);
result[payloadEnd - headerEnd] = '\0';
free(buffer);
buffer = NULL;
return result;
}
char* getFact(void) {
char* page = downloadHTTP("www.randomfunfacts.com", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
char* getSlogan(void) {
char* page = downloadHTTP("thesurrealist.co.uk", "slogan.cgi");
if(page == NULL)
return NULL;
char* first = strstr(page, "class=\"shirtaw\">");
if(first == NULL)
return NULL;
first += 16;
char* last = strstr(first, "</a>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
char* getInsult(void) {
char* page = downloadHTTP("www.randominsults.net", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
char* getQuote(void) {
char* page = downloadHTTP("www.quotability.com", "");
if(page == NULL)
return NULL;
char* first = strstr(page, "<i>");
if(first == NULL)
return NULL;
first += 3;
char* last = strstr(first, "</i>");
if(last == NULL)
return NULL;
int len = last - first;
char* result = (char*)malloc(len + 1);
memcpy(result, first, len);
result[len] = '\0';
free(page);
return result;
}
//IRC processing happens below
int connectToIRC(char* server, int port) {
printf("Connecting to %s:%i\n", server, port);
struct hostent* host = gethostbyname(server);
if(host == NULL) {
#ifdef __unix__
herror("Error resolving host name");
#else
printf("Error resolving host name. errorcode: %i\n", WSAGetLastError());
#endif
return -1;
}
struct sockaddr_in address;
address.sin_family = AF_INET;
address.sin_port = htons(port);
address.sin_addr.s_addr = ((struct in_addr*)host->h_addr)->s_addr;
int sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sock_fd < 0) {
perror("failed to open socket");
return -1;
}
if(connect(sock_fd, (struct sockaddr*)&address, sizeof(address)) < 0) {
perror("failed to connect");
return -1;
}
return sock_fd;
}
struct BufferedSocketReader {
int sock_fd;
int closed; //is set if the file is closed AND the buffer is empty
char* buffer; //note buffer isnt null-terminated
int pos;
int len;
};
void sendData(struct BufferedSocketReader* reader, char* line) {
//makes a copy of the line with a linebreak added
int lbLen = strlen(line) + 2;
char* lineLB = (char*)malloc(lbLen);
memcpy(lineLB, line, lbLen - 1);
lineLB[lbLen - 2] = '\n';
lineLB[lbLen - 1] = '\0';
printf("=> %s", lineLB);
send(reader->sock_fd, lineLB, strlen(lineLB), 0); //TODO check return value and resend if not everything is sent
free(lineLB);
}
void closeSocketReader(struct BufferedSocketReader* reader) {
#ifdef __unix__
close(reader->sock_fd);
#else
closesocket(reader->sock_fd);
#endif
reader->sock_fd = -1;
}
void fillSocketBuffer(struct BufferedSocketReader* reader) {
if(reader->sock_fd == -1) {
reader->closed = 1;
return;
}
char buf[BUFFER_SIZE];
int recLen = recv(reader->sock_fd, buf, BUFFER_SIZE, 0);
if(recLen < 0) {
closeSocketReader(reader);
}
int newLen = (reader->len - reader->pos) + recLen; //new length is the length already in the buffer, and length of just recved
char* newBuf = (char*)malloc(newLen);
memset(newBuf, '\0', newLen);
memcpy(newBuf, reader->buffer + reader->pos, reader->len - reader->pos); //copy the old buffer into new
memcpy(newBuf + reader->len - reader->pos, buf, recLen); //copy receved buffer into new buffer
free(reader->buffer);
reader->buffer = newBuf;
reader->len = newLen;
reader->pos = 0;
}
char* readNextLine(struct BufferedSocketReader* reader) {
//bug, if we reach the EOF and there is no line break, the data in the buffer will be discarded
//not likely to happen on irc protocol though
while(!reader->closed) {
for(int i = reader->pos; i < reader->len-1; i++) { //-1 to allow for the check for double linebreak
if(reader->buffer[i] == '\r' || reader->buffer[i] == '\n') {
int len = i - reader->pos;
char* result = (char*)malloc(len + 1);
memcpy(result, reader->buffer + reader->pos, len);
result[len] = '\0';
reader->pos = i + 1; //skip the char you just read
//sometimes line breaks come in twos
if(reader->buffer[reader->pos] == '\r' || reader->buffer[reader->pos] == '\n')
reader->pos++;
return result;
}
}
//didnt find \r\n, need to read more data into the buffer
fillSocketBuffer(reader);
}
//socket is closed and buffer is empty
return NULL;
}
void handleCommand(struct BufferedSocketReader* reader, char* nick, char* user, char* host, char* cmd, char* chan, char* text) {
#ifdef NEED_MASTER
if(strstr(host, MASTER_HOST) == NULL)
return;
#endif
if(strstr(chan, CHANNEL) == NULL) //someone sent private message, ie. not from channel
return;
if(strstr(text, "quit") == text && strstr(host, MASTER_HOST) != NULL) {
sendData(reader, "QUIT :leaving");
printf("Quitting\n");
closeSocketReader(reader);
}else
if(strstr(text, "say") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(text + 4) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, text + 4);
sendData(reader, sayline);
free(sayline);
}else
if(strstr(text, "fact") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* fact = getFact();
if(fact == NULL)
fact = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(fact) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, fact);
sendData(reader, sayline);
free(fact);
free(sayline);
}else
if(strstr(text, "slogan") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* slogan = getSlogan();
if(slogan == NULL)
slogan = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(slogan) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, slogan);
sendData(reader, sayline);
free(slogan);
free(sayline);
}else
if(strstr(text, "insult") == text) {
char* target = NULL;
if(strchr(text, ' ') != NULL) {
char* t = strchr(text, ' ');
*t = '\0';
target = t + 1;
//if there is a trailing ' ' we have to remove
if(strchr(target, ' ') != NULL) {
char* t = strchr(target, ' ');
*t = '\0';
}
}
char* cmd = "PRIVMSG ";
char* colon = " :";
char* insult = getInsult();
if(insult == NULL)
insult = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) +
(target == NULL ? 0 : strlen(target) + 2) + strlen(insult) + 1;
//add 2 if there is a target, for the colon and space
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
if(target != NULL) {
strcat(sayline, target);
strcat(sayline, ": ");
}
strcat(sayline, insult);
sendData(reader, sayline);
free(insult);
free(sayline);
}else
if(strstr(text, "quote") == text) {
char* cmd = "PRIVMSG ";
char* colon = " :";
char* quote = getQuote();
if(quote == NULL)
quote = malloc(1); //to stop free() segfaulting
int len = strlen(cmd) + strlen(CHANNEL) + strlen(colon) + strlen(quote) + 1;
char* sayline = (char*)malloc(len);
*sayline = '\0';
strcat(sayline, cmd);
strcat(sayline, CHANNEL);
strcat(sayline, colon);
strcat(sayline, quote);
sendData(reader, sayline);
free(quote);
free(sayline);
}
}
void handleLine(struct BufferedSocketReader* reader, char* line) {
printf("<= %s\n", line);
char* lineTok = (char*)malloc(strlen(line) + 1);
strcpy(lineTok, line);
if(strstr(line, "PING") == line) {
line[1] = 'O';
sendData(reader, line);
printf("Replied to ping\n");
free(lineTok);
return;
}
//has the ":nick!username@host" format
//also works for other stuff
if(*lineTok != ':') {
free(lineTok);
return;
}
char* nickuserhost = strtok(lineTok + 1, " ");
char* cmd = strtok(NULL, " ");
char* chan = strtok(NULL, " ");
char* text = strtok(NULL, "\0");
char* nick = NULL;
char* user = NULL;
char* host = NULL;
//split nick, user and host
if(nickuserhost != NULL && strchr(nickuserhost, '!') != NULL && strchr(nickuserhost, '@') != NULL) {
nick = strtok(nickuserhost, "!");
user = strtok(NULL, "@");
host = strtok(NULL, "\0");
}
if(text != NULL && *text == ':') //remove the first ':' on chat
text++;
//below is the using of the strings
if(strcmp(cmd, "PRIVMSG") == 0 && *text == COMMAND_PREFIX) { //someone has spoken
handleCommand(reader, nick, user, host, cmd, chan, text + 1);
}
if(strcmp(cmd, "KICK") == 0 && strstr(text, NICK) == text) {
char* header = "JOIN "; //send "JOIN #channel"
char* chanLine = (char*)malloc(strlen(header) + strlen(CHANNEL) + 1);
*chanLine = '\0';
strcat(chanLine, header);
strcat(chanLine, CHANNEL);
sendData(reader, chanLine);
free(chanLine);
}
free(lineTok);
}
int main(int argc, char** argv) {
#ifdef __WIN32__
WSADATA wsaData;
if(WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
printf("Error starting winsock\n");
return EXIT_FAILURE;
}
#endif
struct BufferedSocketReader reader;
reader.sock_fd = connectToIRC(IRC_SERVER, IRC_PORT);
reader.closed = 0;
reader.pos = 0;
sendData(&reader, "USER user 8 * :real");
char* header = "NICK "; //send "NICK mynick"
char* nickLine = (char*)malloc(strlen(header) + strlen(NICK) + 1);
*nickLine = '\0';
strcat(nickLine, header);
strcat(nickLine, NICK);
sendData(&reader, nickLine);
free(nickLine);
header = "JOIN "; //send "JOIN #channel"
char* chanLine = (char*)malloc(strlen(header) + strlen(CHANNEL) + 1);
*chanLine = '\0';
strcat(chanLine, header);
strcat(chanLine, CHANNEL);
sendData(&reader, chanLine);
free(chanLine);
char buf[BUFFER_SIZE];
reader.len = recv(reader.sock_fd, buf, BUFFER_SIZE, 0);
reader.buffer = (char*)malloc(reader.len); //initalise buffer
memcpy(reader.buffer, buf, reader.len);
while(!reader.closed) {
char* line = readNextLine(&reader);
if(line == NULL)
continue;
handleLine(&reader, line);
free(line);
}
printf("Socket closed or buffer empty\nExiting...\n");
#ifdef __WIN32__
WSACleanup();
system("PAUSE");
#endif
return EXIT_SUCCESS;
}