/*
Nick holder
Jinx
*/
#ifndef WIN32 /* Not Win32 */
#include <stdio.h>
#include <pcreposix.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <pthread.h>
#define SOCKET_ERROR -1
#define NEWTHREAD(a,b) pthread_create(&threadid, 0, (void *)&a, (void *)b)
#else /* Win32 specific defines */
#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <pcreposix.h>
#include <time.h>
#include <io.h>
#include <fcntl.h>
#undef fdopen /* Undefine first */
#define fdopen(a,b) _fdopen(_open_osfhandle((SOCKET)a, _O_RDONLY|_O_BINARY), b)
#define sleep(a) Sleep(1000*a)
#define pthread_t char /* Let's just make it a char */
#define NEWTHREAD(a,b) CreateThread(0, 0, (LPTHREAD_START_ROUTINE)&a, (LPVOID)b, 0, 0)
#endif /* End compatibility defines */
#define WHOIS_CMD "WHOIS %s\n%c"
#define MOTD_END 376
#define NICKNAME_IN_USE 433
#define NO_SUCH_NICKNAME 401
#define DEFAULT_IDENT "nkeep"
#define DEFAULT_FULLNAME "Nick Keeper by Jinx"
#define DEFAULT_PORT 6667
#define DEFAULT_WHOISDELAY 60
#define DEFAULT_CONNECTDELAY 5
#define DEFAULT_LOGGING 0
/* User Settings */
char MyNickname[100];
char PrimaryNickname[100];
char AlternateNickname[100];
char ServerName[100];
char ActiveChannel[100];
char MainChannel[100];
char Ident[20];
char FullName[100];
int Port;
int WhoisDelay;
int ConnectDelay;
int Logging;
int s;
int connected;
FILE *logfile = NULL;
enum IRCMatchNames
{
MATCH_ALL,
MATCH_ADDR_ALL,
MATCH_ADDR_NICK,
MATCH_ADDR_END,
MATCH_ADDR_IDENT,
MATCH_ADDR_DOMAIN,
MATCH_NUMERIC,
MATCH_NULL,
MATCH_TARGET,
MATCH_DATA,
MATCH_EXTRA,
NUM_MATCHES
};
regex_t *linematch;
char *maketimestamp()
{
char *timestamp = malloc(8);
time_t timenow = time(NULL);
struct tm *t = malloc(sizeof(struct tm));
t = localtime(&timenow);
sprintf(timestamp, "[%02d:%02d]", t->tm_hour, t->tm_min);
return timestamp;
}
#ifdef DEBUG /* DEBUGGING ONLY */
char *GetMatchName(int i)
{
switch(i)
{
case 0: return "MATCH_ALL";
case 1: return "MATCH_ADDR_ALL";
case 2: return "MATCH_ADDR_NICK";
case 3: return "MATCH_ADDR_END";
case 4: return "MATCH_ADDR_IDENT";
case 5: return "MATCH_ADDR_DOMAIN";
case 6: return "MATCH_NUMERIC";
case 7: return "MATCH_NULL";
case 8: return "MATCH_TARGET";
case 9: return "MATCH_DATA";
case 10: return "MATCH_EXTRA";
}
return "";
}
#endif /* DEBUG END */
int copymatches(char *buffer, regmatch_t *copy_matches, int copysize, char **copy_match_strings)
{
int i, length;
for (i = 0; i < copysize; i++)
{
if (copy_matches[i].rm_eo >= 0)
{
length = copy_matches[i].rm_eo - copy_matches[i].rm_so;
copy_match_strings[i] = (char *)malloc(length + 1);
strncpy(copy_match_strings[i], buffer+copy_matches[i].rm_so, length);
*(copy_match_strings[i]+length) = '\0';
}
#ifdef DEBUG /* DEBUGGING ONLY */
else
{
copy_match_strings[i] = (char *)malloc(1);
*copy_match_strings[i] = '\0';
}
printf("match[%s]\t\t-\t\"%s\"\n", GetMatchName(i), copy_match_strings[i]);
#endif /* DEBUG END */
}
return 0;
}
/***********************
TimerCheckPrimaryNick:
Performs a WHOIS command
on PrimaryNick every WhoisDelay seconds
***********************/
int TimerCheckPrimaryNick()
{
char *cmd = malloc(strlen(WHOIS_CMD) + strlen(PrimaryNickname) + 1);
while(1)
{
sleep(WhoisDelay); /* compatibility macro */
sprintf(cmd, WHOIS_CMD, PrimaryNickname, NULL);
if (connected) send(s, cmd, strlen(cmd), 0);
}
}
int readline(char *inbuf, FILE *sockfd)
{
int len;
#ifndef WIN32 /* Not Win32 */
if (fgets(inbuf, 4096, sockfd) == NULL)
{
return -1;
}
#else
int r, bytes = 0;
memset(inbuf, 0, 4096);
do
{
if ((r = recv(s, (inbuf+bytes), 1, 0)) == SOCKET_ERROR)
{
return -1;
}
bytes += r;
} while (*(inbuf+bytes-1) != '\n');
#endif
len = strlen(inbuf) - 1;
*(inbuf+len) = '\0';
if (*(inbuf+len-1) == '\r') *(inbuf+len-1) = '\0';
return 0;
}
/***********************
SocketHandler:
Main socket thread. Handles
all socket data from the server.
***********************/
int SocketHandler(int delay)
{
struct hostent *h;
struct sockaddr_in addr;
char *timestamp, *event;
int numeric = 0;
pthread_t threadid = 0;
char *match_strings[NUM_MATCHES];
regmatch_t *matches = malloc(NUM_MATCHES * sizeof(regmatch_t) + 1);
char *inbuf = malloc(4096);
char *outbuf = malloc(4096);
FILE *sockfd;
// Setup the socket
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR)
{
perror("Could not create socket");
return 0;
}
h = gethostbyname(ServerName);
memcpy(&addr.sin_addr.s_addr, h->h_addr_list[0], sizeof(h->h_addr_list));
addr.sin_port = htons((unsigned short)Port);
addr.sin_family = AF_INET;
// sleep for delay seconds
if (delay)
{
timestamp = maketimestamp();
printf("%s * Sleeping for %d seconds before next connection...\n", timestamp, delay);
sleep(delay);
}
timestamp = maketimestamp();
printf("%s * Connecting to %s...\n", timestamp, ServerName);
if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR)
return 0;
sprintf(outbuf, "NICK %s\nUSER %s %s %s :%s\n", PrimaryNickname, Ident, Ident, ServerName, FullName);
send(s, outbuf, strlen(outbuf), 0);
sockfd = fdopen(s, "r"); /* compatibility macro */
while(1)
{
// Get time
timestamp = maketimestamp();
// Read Line by Line
if (readline(inbuf, sockfd) == SOCKET_ERROR)
{
printf("%s ERROR! * Disconnected, retrying connection...\n", timestamp);
connected = 0;
NEWTHREAD(SocketHandler, ConnectDelay);
return 0;
}
*outbuf = '\0';
//#ifdef DEBUG /* DEBUGGING ONLY */
// Output all server data
printf("%s [DEBUG] %s\n", timestamp, inbuf);
//#endif
// Get data into variables
if (regexec(linematch, inbuf, NUM_MATCHES, matches, 0) == REG_NOMATCH)
{
#ifdef DEBUG /* DEBUGGING ONLY */
printf("%s [DEBUG] No match\n", timestamp);
#endif
}
else
{
copymatches(inbuf, matches, NUM_MATCHES, match_strings);
if ((numeric = atoi(match_strings[MATCH_NUMERIC])) > 0)
{
// NUMERIC Event handling
switch(numeric)
{
case NO_SUCH_NICKNAME:
if (strncmp(match_strings[MATCH_DATA], PrimaryNickname, strlen(PrimaryNickname)) == 0)
{
printf("%s [COMMAND] * %s is not taken, changing nickname...\n", timestamp, PrimaryNickname);
sprintf(outbuf, "NICK %s\n%c", PrimaryNickname, NULL);
}
break;
case NICKNAME_IN_USE:
if (!connected)
{
if (strcmp(MyNickname, PrimaryNickname) == 0)
{
sprintf(outbuf, "NICK %s\n%c", AlternateNickname, NULL);
}
else
{
sprintf(outbuf, "NICK %s\n%c", PrimaryNickname, NULL);
}
}
break;
case MOTD_END:
if (connected) break;
connected = 1;
strcpy(ServerName, match_strings[MATCH_ADDR_NICK]);
printf("%s * Connected to %s:%d\n", timestamp, ServerName, Port);
strcpy(MyNickname, match_strings[MATCH_TARGET]);
if (*MainChannel != '\0')
{
printf("%s [COMMAND] * Joining (MAIN_CHANNEL) %s...\n", timestamp, MainChannel);
sprintf(outbuf, "JOIN %s\n%c", MainChannel, '\0');
}
break;
default:
break;
}
}
else
{
event = match_strings[MATCH_NUMERIC];
// TEXT Event handling
if (*event != '\0')
{
if (strcmp(event, "NICK") == 0)
{
printf("%s * %s is now known as %s\n", timestamp, match_strings[MATCH_ADDR_NICK], match_strings[MATCH_DATA]);
if (strcmp(match_strings[MATCH_ADDR_NICK], MyNickname) == 0)
{
strcpy(MyNickname, match_strings[MATCH_DATA]);
}
}
else if (strcmp(event, "QUIT") == 0)
{
printf("%s * Quits: %s (%s) (%s)\n", timestamp, match_strings[MATCH_ADDR_NICK],
match_strings[MATCH_ADDR_ALL], match_strings[MATCH_DATA]);
if (strcmp(match_strings[MATCH_ADDR_NICK], PrimaryNickname) == 0)
{
printf("%s [COMMAND] * Primary nickname %s has left the building, changing nickname...\n", timestamp, PrimaryNickname);
sprintf(outbuf, "NICK %s\n%c", PrimaryNickname, NULL);
}
}
else if (strcmp(event, "JOIN") == 0)
{
printf("%s * Joins: %s (%s) on %s\n", timestamp, match_strings[MATCH_ADDR_NICK],
match_strings[MATCH_ADDR_ALL], match_strings[MATCH_DATA]);
if (strcmp(match_strings[MATCH_ADDR_NICK], MyNickname) == 0) // I joined
{
strcpy(ActiveChannel, match_strings[MATCH_DATA]);
}
}
else if (strcmp(event, "PRIVMSG") == 0) // Message
{
if (strcmp(match_strings[MATCH_TARGET], MyNickname) == 0) // Private
{
printf("%s -> *%s* %s\n", timestamp, match_strings[MATCH_ADDR_NICK], match_strings[MATCH_DATA]);
if (strcmp(match_strings[MATCH_DATA], "!off") == 0 && strcmp(MyNickname, AlternateNickname) != 0)
{
printf("%s [COMMAND] * %s wants me off %s, changing nickname...\n", timestamp, match_strings[MATCH_ADDR_NICK], MyNickname);
sprintf(outbuf, "NICK %s\n%c", AlternateNickname, NULL);
}
if (strcmp(match_strings[MATCH_DATA], "!on") == 0 && strcmp(MyNickname, PrimaryNickname) != 0)
{
printf("%s [COMMAND] * %s wants me on %s, attempting to change nickname...\n", timestamp, match_strings[MATCH_ADDR_NICK], PrimaryNickname);
sprintf(outbuf, "NICK %s\n%c", PrimaryNickname, NULL);
}
}
else if (*match_strings[MATCH_TARGET] == '#') // Channel
{
printf("%s <%s:%s> %s\n", timestamp, match_strings[MATCH_ADDR_NICK], match_strings[MATCH_TARGET], match_strings[MATCH_DATA]);
}
}
}
}
}
// Send data if we have any on queue
if (*outbuf)
{
send(s, outbuf, strlen(outbuf), 0);
}
}
fclose(sockfd);
return 0;
}
int parse_config(char *filename)
{
FILE *fp = fopen(filename, "r");
char line[4096], setting[100], value[100];
int r, i;
if (fp == NULL)
{
printf("ERROR! Config file %s not found.\n", filename);
return -1;
}
while(fgets(line, 4096, fp) != NULL)
{
r = sscanf(line, "%[^=]=%[^\r\n]", setting, value);
if (r == 0) continue;
sscanf(setting, "%s", setting);
for(i = 0; i < (int)strlen(setting); i++) setting[i] = tolower(setting[i]);
// Check settings
if (!strcmp(setting, "server"))
{
strcpy(ServerName, value);
}
else if (!strcmp(setting, "port"))
{
Port = atoi(value);
}
else if (!strcmp(setting, "pnick"))
{
strcpy(PrimaryNickname, value);
}
else if (!strcmp(setting, "anick"))
{
strcpy(AlternateNickname, value);
}
else if (!strcmp(setting, "whois-delay"))
{
WhoisDelay = atoi(value);
}
else if (!strcmp(setting, "connect-delay"))
{
ConnectDelay = atoi(value);
}
else if (!strcmp(setting, "channel"))
{
strcpy(MainChannel, value);
}
else if (!strcmp(setting, "ident"))
{
strcpy(Ident, value);
}
else if (!strcmp(setting, "fullname"))
{
strcpy(FullName, value);
}
}
return 0;
}
int usage(int argc, int i, char *program)
{
if (i+1 >= argc)
{
printf("usage: %s [-c config-file] [-s server] [-p port] [-n primary-nick]\n", program);
printf("[-a alternate-nick] [-m main-channel] [-i ident] [-f fullname]\n");
printf("[--whois-delay delay] [--connect-delay delay] [--?]\n\n");
printf("Main Options:\n");
printf("-c\t--config-file\tuse another configuration file (default=nkeep.conf)\n");
printf("-h\t--?\t\tprint this help.\n\n");
printf("Override Options: (Use these to override the config file)\n");
printf("-s\t--server\toverride server ip in configuration.\n");
printf("-p\t--port\t\toverride default port.\n");
printf("-n\t--pnick\t\tnickname to keep.\n");
printf("-a\t--anick\t\tnickname to sit on when pnick is taken.\n");
printf("-m\t--channel\tchannel to idle in.\n");
printf("-i\t--ident\t\toverride default ident.\n");
printf("-f\t--fullname\toverride full name.\n");
printf("-w\t--whois-delay\toverride whois delay time.\n");
printf("-d\t--connect-delay\toverride reconnect delay time.\n");
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
char input[4096], *outbuffer = malloc(4096);
int re = 0, i;
regmatch_t *matches = malloc(NUM_MATCHES * sizeof(regmatch_t) + 1);
pthread_t threadid = 0;
#ifdef WIN32 /* Win32 - Starting WSA */
WSADATA wsaData;
WORD wVersionRequested = MAKEWORD( 1, 1 );
if(WSAStartup(wVersionRequested, &wsaData ) != 0)
{
return 0;
}
#endif
/* Default settings */
strcpy(Ident, DEFAULT_IDENT);
strcpy(FullName, DEFAULT_FULLNAME);
*MainChannel = '\0';
Port = DEFAULT_PORT;
WhoisDelay = DEFAULT_WHOISDELAY;
ConnectDelay = DEFAULT_CONNECTDELAY;
Logging = DEFAULT_LOGGING;
printf("Nickname Keeper by Jinx\n");
if (argc < 2) // Use default config
{
printf("Loading default configuration file...\n");
if (parse_config("nkeep.conf") < 0) return 0;
}
else
{
parse_config("nkeep.conf"); // Load default settings, if any.
for (i = 1; i < argc; i++)
{
if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--config"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
if (parse_config(argv[i+1]) < 0) return 0;
}
else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--server"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
strcpy(ServerName, argv[i+1]);
}
else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
Port = atoi(argv[i+1]);
}
else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--connect-delay"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
ConnectDelay = atoi(argv[i+1]);
}
else if (!strcmp(argv[i], "-n") || !strcmp(argv[i], "--pnick"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
strcpy(PrimaryNickname, argv[i+1]);
}
else if (!strcmp(argv[i], "-a") || !strcmp(argv[i], "--anick"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
strcpy(AlternateNickname, argv[i+1]);
}
else if (!strcmp(argv[i], "-w") || !strcmp(argv[i], "--whois-delay"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
WhoisDelay = atoi(argv[i+1]);
}
else if (!strcmp(argv[i], "-m") || !strcmp(argv[i], "--channel"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
strcpy(MainChannel, argv[i+1]);
}
else if (!strcmp(argv[i], "-i") || !strcmp(argv[i], "--ident"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
strcpy(Ident, argv[i+1]);
}
else if (!strcmp(argv[i], "-f") || !strcmp(argv[i], "--fullname"))
{
if (usage(argc, i, argv[0]) < 0) return 0;
strcpy(FullName, argv[i+1]);
}
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")
|| *argv[i-1] != '-' || *argv[i] == '-') // None of the above
{
usage(0, 0, argv[0]);
return 0;
}
}
}
if (*ServerName == '\0' || Port < 0 || Port > 65536 || *PrimaryNickname == '\0' || *AlternateNickname == '\0')
{
printf("ERROR! Missing server or nickname information.\n");
return 0;
}
strcpy(MyNickname, PrimaryNickname);
// Compile regex match
linematch = malloc(sizeof(regex_t));
if (regcomp(linematch, "^:(([^!\\s]+)(!([^@]+)@([^\\s]+))?)\\s+([^\\s]+)\\s+(([^\\s]+)\\s+)?:?(.+)$", 0) != 0)
{
return 0;
}
connected = 0;
printf("Loaded nkeep with settings:\n\nSERVER=%s:%d PNICK=%s ANICK=%s\nWHOISDELAY=%d CONNECTDELAY=%d CHANNEL=%s IDENT=%s\nFULLNAME=%s\n\n",
ServerName, Port, PrimaryNickname, AlternateNickname, WhoisDelay, ConnectDelay, MainChannel, Ident, FullName);
// Create threads
NEWTHREAD(TimerCheckPrimaryNick, 0); /* compatibility macro */
NEWTHREAD(SocketHandler, 0); /* compatibility macro */
while(1)
{
fgets(input, 4096, stdin);
*(input+strlen(input)-1) = '\0';
memset(outbuffer, 0, 4096);
if (input[0] == '/')
{
strcpy(outbuffer, &input[1]);
}
else
{
if (connected && *ActiveChannel != '\0')
{
sprintf(outbuffer, "PRIVMSG %s :%s\n", ActiveChannel, input);
}
}
send(s, outbuffer, strlen(outbuffer), 0);
}
return 0;
}
Powered by
GeSHi Syntax Highlighting software.
Author of all (other) material unless otherwise specified:
Loren Segal. Copyright 2005.