diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0435758 --- /dev/null +++ b/Makefile @@ -0,0 +1,46 @@ +# $Id$ +# Application Makefile (C) 2002-2004 The UbixOS Project + +#Compiler +CC = gcc +CXX = g++ + +#Linker +LD = ld + +#Binary File Name +BINARY = tuved + +#Delete Program +REMOVE = rm -f + +#Objects +OBJS = mysql.o server.o nick.o botthread.o main.o socket.o tuve.o channel.o + +LIBRARIES = -lthr -L/usr/local/lib/mysql -lmysqlclient +CFLAGS = -Wall -W -ggdb +INCLUDES = -I/usr/local/include + +# Link The Binary +$(BINARY) : $(OBJS) + $(CC) $(CFLAGS) -o $@ $(LIBRARIES) $(OBJS) + +# Compile the source files +.cc.o: + $(CXX) $(CFLAGS) $(INCLUDES) -c -o $@ $< + +.cc.s: + $(CXX) $(CFLAGS) $(INCLUDES) -S -o $@ $< + +.c.o: + $(CC) $(CFLAGS) $(INCLUDES) -c -o $@ $< + +.c.s: + $(CC) $(CFLAGS) $(INCLUDES) -S -o $@ $< + +.S.o: + $(CC) $(CLFAGS) $(INCLUDES) -c -o $@ $< + +# Clean Up The junk +clean: + $(REMOVE) $(OBJS) $(BINARY) *.core diff --git a/main.c b/main.c new file mode 100644 index 0000000..ab145a2 --- /dev/null +++ b/main.c @@ -0,0 +1,134 @@ +/* + Tuved Server + + $Id$ +*/ + +#include +#include +#include +#include + +#include "tuved.h" + +FILE *logFile = 0x0; +int logLevel = 1; +pthread_mutex_t chanMutex = PTHREAD_MUTEX_INITIALIZER; +time_t startTime; +int userCount = 0; +int channelCount = 0; +int songCount = 0; +int playCount = 0; + +void usage(); + +int main(int argc,char **argv) { + fd_set readset; + int ch; + int readSocks = 0x0; + int doFork = 0x1; + int rc = 0x0; + int t = 0x0; + time_t seconds = 0; + struct timeval timeout = {30,0}; + + /* Get our start time */ + startTime = time(NULL); + + while ((ch = getopt(argc,argv,"sl:nd:")) != -1) { + switch (ch) { + case 's': + logFile = stdout; + break; + case 'l': + logFile = fopen(optarg,"a"); + break; + case 'n': + doFork = 0; + break; + case 'd': + logLevel = atoi(optarg); + if (logLevel < 1) + logLevel = 1; + break; + default: + usage(); + } + } + argc -= optind; + argv += optind; + + + /* Prepare log file */ + if (logFile == 0x0) + logFile = fopen(LOG_FILE,"a"); + + /* Fork into background unless specified not to do so */ + if (doFork == 1) + if (fork() != 0x0) + exit(1); + + writeLog(0,"Podz Daemon Starting: %i\n",startTime); + + dbInit(); + + srandom(time(NULL)); + + pthread_mutex_init(&chanMutex, NULL); + + pthread_t threads[NUM_THREADS]; + rc = pthread_create(&threads[t], NULL, tuveBotThread, (void *)t); + + + + sStartListener(); + + while (1) { + sGetConnections(&readset); + + readSocks = select(highSock+1,&readset,0x0,0x0,&timeout); + + writeLog(3,"readSocks: [%i]\n",readSocks); + + if (readSocks < 0) { + perror("select failed"); + exit(0x1); + } + else if (readSocks > 0) { + sProcessConnections(&readset); + } + + if ((seconds + PING_INTERVAL) < time(NULL)) { + sSendPing(seconds); + seconds = time(NULL); + } + + /* Clean up old connections */ + sCleanConnections(); + + /* Flush the log file */ + fflush(logFile); + } + + return(0); + } + +void usage() { + printf("%s\n","usage: tuved -n -s [-l logfile] [-d debuglevel]"); + exit(1); + } + + +int writeLog(int level,char const * __restrict fmt, ...) { + int ret; + va_list ap; + + if (level > logLevel) + return(0x0); + + va_start(ap, fmt); + ret = vfprintf(logFile, fmt, ap); + va_end(ap); + + return(ret); + } diff --git a/socket.c b/socket.c new file mode 100644 index 0000000..574bf27 --- /dev/null +++ b/socket.c @@ -0,0 +1,281 @@ +/* + podzd - Socket Server + (c) 2007 Christopher Olsen + + $Id$ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tuved.h" + +myConnections_t *connections = 0x0; + +int listenerFD = 0x0; +int highSock = 0x0; + +int sStartListener() { + int optVal = 0x1; + + struct sockaddr_in myAddr; // my address information + + if ((listenerFD = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + perror("socket"); + exit(1); + } + + if (setsockopt(listenerFD, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(int)) == -1) { + perror("setsockopt"); + exit(1); + } + + myAddr.sin_family = AF_INET; // host byte order + myAddr.sin_port = htons(MYPORT); // short, network byte order + myAddr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP + memset(myAddr.sin_zero, '\0', sizeof myAddr.sin_zero); + + if (bind(listenerFD, (struct sockaddr *)&myAddr, sizeof(struct sockaddr)) == -1) { + perror("bind"); + exit(1); + } + + if (listen(listenerFD, BACKLOG) == -1) { + perror("listen"); + exit(1); + } + + fcntl(listenerFD, F_SETFL, fcntl(listenerFD, F_GETFL, 0) | O_NONBLOCK); + + sAddConnection(listenerFD,"127.0.0.1"); + + writeLog(2,"Listener FD: [%i]\n",listenerFD); + + return(0x0); + } + +ssize_t sReadSocket(int socketFD,void *buffer,size_t length) { + ssize_t recLen = 0x0; + + recLen = read(socketFD,buffer,length); + + if (recLen <= 2) + recLen = 0x0; + + return(recLen); + } + +int sGetConnection() { + int newFD = 0x0; // New Socket; + socklen_t sin_size; + struct sockaddr_in remoteAddr; + struct hostent *hp; + sin_size = sizeof(struct sockaddr_in); + + if ((newFD = accept(listenerFD, (struct sockaddr *)&remoteAddr, &sin_size)) == -1) { + perror("accept"); + } + + + /* Add Socket */ + hp = gethostbyaddr((char *)&remoteAddr.sin_addr,sizeof(remoteAddr.sin_addr),AF_INET); + sAddConnection(newFD,(hp ? hp->h_name : inet_ntoa(remoteAddr.sin_addr))); + + /* Return */ + return(0x0); + } + +int sAddConnection(int socketFD,char *host) { + myConnections_t *tmpConnection = 0x0; + + writeLog(2,"Adding Socket: [%i]\n",socketFD); + + userCount++; + + if (connections == 0x0) { + connections = (myConnections_t *)calloc(1,sizeof(myConnections_t)); + connections->prev = 0x0; + connections->next = 0x0; + connections->fd = socketFD; + connections->signedOn = time(NULL); + sprintf(connections->host,host); + } + else { + tmpConnection = (myConnections_t *)calloc(1,sizeof(myConnections_t)); + //memset(tmpConnection,0x0,sizeof(myConnections_t)); + tmpConnection->fd = socketFD; + tmpConnection->prev = connections; + tmpConnection->next = connections->next; + tmpConnection->signedOn = time(NULL); + sprintf(tmpConnection->host,host); + if (connections->next != 0x0) + connections->next->prev = tmpConnection; + + connections->next = tmpConnection; + } + writeLog(2,"Socket Added\n"); + + return(0x0); + } + +myConnections_t *sFindConnection(int fd) { + myConnections_t *tmpConnection = 0x0; + + for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) { + if (tmpConnection->fd == fd) + return(tmpConnection); + } + + return(0x0); + } + +int sCleanConnections() { + myConnections_t *tmpConnection = 0x0; + myConnections_t *delConnection = 0x0; + + for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) { + if (tmpConnection->userInfo.status == -1) { + // Set temporary pointer to connection so we can free it later + delConnection = tmpConnection; + + // Hard close the socket so it doesn't linger + close(tmpConnection->fd); + + writeLog(2,"Removed User: [%s]\n",tmpConnection->userInfo.username); + + // If connection has a previous pointer which it always should adjust it's next pointer to match current next + if (tmpConnection->prev != 0x0) + tmpConnection->prev->next = tmpConnection->next; + + // If connection has a next pointer which it may not adjust it to point to match our current previous + if (tmpConnection->next != 0x0) + tmpConnection->next->prev = tmpConnection->prev; + + // Adjust tmpConnection to match our previous pointer which should always happen + if (tmpConnection->prev != 0x0) + tmpConnection = tmpConnection->prev; + else + tmpConnection = tmpConnection->next; + + userCount--; + + //Free the bad connection + free(delConnection); + writeLog(2,"Remove Completed\n"); + } + } + return(0x0); + } + +int sRemoveConnection(int socketFD) { + myConnections_t *tmpConnection = 0x0; + writeLog(2,"Removing Socket: socketFD\n"); + + for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) { + if (tmpConnection->fd == socketFD) { + + tmpConnection->prev->next = tmpConnection->next; + + if (tmpConnection->next != 0x0) + tmpConnection->next->prev = tmpConnection->prev; + writeLog(2,"Removed Socket: [%i]\n",tmpConnection->fd); + + userCount--; + + free(tmpConnection); + return(0x0); + } + } + + writeLog(2,"Error: No Socket Removed\n"); + return(-1); + } + +int sGetConnections(fd_set *readset) { + int retVal = 0; + myConnections_t *tmpConnection = 0x0; + + FD_ZERO(readset); + +// FD_SET(listenerFD,readset); + + highSock = 0; + + if (connections != 0x0) { + for (tmpConnection = connections;tmpConnection != 0x0;tmpConnection = tmpConnection->next) { + FD_SET(tmpConnection->fd,readset); + if (tmpConnection->fd > highSock) + highSock = tmpConnection->fd; + } + retVal = 1; + } + + return(retVal); + } + +/************************************ + * * + * Send ping broadcast to all users * + * * + ************************************/ +int sSendPing(time_t ping) { + myConnections_t *tmpConnection = 0x0; + + for (tmpConnection = connections->next;tmpConnection != 0x0;tmpConnection = tmpConnection->next) { + if (tmpConnection->userInfo.pfailed < MAX_PING) { + if (tmpConnection->userInfo.pong < ping) { + tmpConnection->userInfo.pfailed++; + tmpConnection->userInfo.pong = ping; + if (tmpConnection->userInfo.ident == 0x1) + send(tmpConnection->fd,"PING\n",sizeof("PING\n"),MSG_NOSIGNAL); + } + } + else { + send(tmpConnection->fd,"TIMEOUT\n",sizeof("TIMEOUT\n"),MSG_NOSIGNAL); + tuveDelUserChans(tmpConnection,"TIMEOUT"); + tmpConnection->userInfo.status = -1; + } + } + + return(0x0); + } /* End sSendPing() */ + +int sSendData(myConnections_t *con,char const *data) { + + if (con == 0x0) + writeLog(1,"Error Sending Data"); + else if (con->userInfo.status == -1) { + writeLog(3,"User: %s Socket Not Avail\n",con->userInfo.username); + return(0x1); + } + + send(con->fd,data,strlen(data),MSG_NOSIGNAL); + return(0x0); + } + +int sProcessConnections(fd_set *readset) { + myConnections_t *tmpConnection = 0x0; + + for (tmpConnection = connections;tmpConnection != 0x0;tmpConnection = tmpConnection->next) { + if (FD_ISSET(tmpConnection->fd,readset) != 0x0) { + writeLog(2,"FD_ISSET\n"); + if (tmpConnection->fd == listenerFD) { + sGetConnection(); + writeLog(2,"Listen Socket Was Ready?\n"); + } + else { + if (podzGetData(tmpConnection) == -1) { + close(tmpConnection->fd); + tuveDelUserChans(tmpConnection,"SIG PIPE"); + sRemoveConnection(tmpConnection->fd); + } + } + } + } + return(0x0); + } diff --git a/tuvebot.h b/tuvebot.h new file mode 100644 index 0000000..9a200fc --- /dev/null +++ b/tuvebot.h @@ -0,0 +1,166 @@ +/* + (c) 2007 Christopher Olsen + + $Id$ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define NUM_THREADS 1 +#define MYPORT 9999 // the port users will be connecting to +#define BACKLOG 10 // how many pending connections queue will hold +#define LOG_FILE "./tuved.log" +#define PING_INTERVAL 20 +#define MAX_PING 2 +#define MAX_COUNT 2 +#define MAX_TOPIC_LEN 128 +#define MAX_CHAN_LEN 32 +#define MAX_USER_LEN 32 +#define MAX_BANS 30 +#define VERSION "v0.40" +#define MYSQL_HOST_NAME "www.ubixonline.com" +#define MYSQL_USERNAME "tuve" +#define MYSQL_PASSWORD "5558585" +#define MYSQL_DB_NAME "tuve" + +#define CHAN_MODES 5 +#define CHAN_RANDOM 0 +#define CHAN_QUEUE 1 +#define CHAN_TIME 2 +#define CHAN_RATING 3 +#define CHAN_CLASS 4 + +typedef struct tuveUserList { + struct tuveUserList *prev; + struct tuveUserList *next; + struct myConnections *user; + } tuveUserList_t; + +typedef struct tuveChanList { + struct tuveChanList *prev; + struct tuveChanList *next; + struct tuveUserList *users; + struct tuveUserList *nextUser; + long long bans[MAX_BANS]; + char modes[CHAN_MODES]; + time_t topicSet; + char topicSetBy[MAX_USER_LEN]; + char topic[MAX_TOPIC_LEN]; + char channel[MAX_CHAN_LEN]; + char curSong[256]; + char songTitle[256]; + int songEnd; + int songTime; + int userCount; + short count; + short maxTime; + } tuveChanList_t; + +typedef struct tuveUserChans { + struct tuveUserChans *prev; + struct tuveUserChans *next; + char channel[32]; + } tuveUserChans_t; + +typedef struct tuveUser { + short ident; + short pfailed; + short status; + short mode; + short queue; + long long uid; + time_t pong; + time_t idle; + char username[MAX_USER_LEN]; + tuveUserChans_t *chans; + } tuveUser_t; + + +typedef struct myConnections { + struct myConnections *prev; + struct myConnections *next; + int fd; + int recLen; + char data[1024]; + char host[128]; + tuveUser_t userInfo; + time_t signedOn; + } myConnections_t; + +/* + Global variables very not safe + */ + +extern int songCount; +extern int playCount; +extern int channelCount; +extern int userCount; +extern int listenerFD; +extern int highSock; +extern myConnections_t *connections; +extern FILE *logFile; +extern tuveChanList_t *channels; +extern pthread_mutex_t chanMutex; +extern time_t startTime; + +/* Socket Functions */ +ssize_t sReadSocket(int socketFD,void *buffer,size_t length); +myConnections_t *sFindConnection(int fd); +int sStartListener(); +int sAddConnection(int,char *host); +int sGetConnections(fd_set *); +int sProcessConnections(fd_set *); +int sSendPing(time_t); +int sCleanConnections(); +int sSendData(myConnections_t *con,char const *data); + +/* Podz Fucntions */ +int podzGetData(myConnections_t *); +int podzProcessData(myConnections_t *); +int podzSendAll(char *,int); + +/* Log Functions */ +int writeLog(int level,char const * __restrict, ...); + +/* Channel Functions */ +int tuveChanList(myConnections_t *userConnection); +int tuveChanPart(const char *channel,myConnections_t *userConnection); +int tuveChanJoin(const char *channel,myConnections_t *userConnection); +int tuveAddChan(const char *channel,myConnections_t *userConnection); +int tuveAddToChanList(const char *channel,myConnections_t *userConnection); +int tuveSendAllInChan(const char *channel,myConnections_t *userConnection,char *output); +int tuveDelUserChans(myConnections_t *userConnection,char *msg); +int tuveRemoveFromChanList(const char *channel,myConnections_t *userConnection); +int tuveSetTopic(myConnections_t *userConnection,char *chan,char *topic); +int tuveSendAllInUsersChans(myConnections_t *userConnection,char *output); +int tuveDelChan(const char *channel,myConnections_t *userConnection); +int tuveKick(char *by,char *chan,char *nick,char *msg); +tuveChanList_t *findChan(const char *); +int tuveBan(const char *channel,const char *nick); +int tuveUnBan(const char *channel,const char *nick); +int tuveVerifyBan(const char *channel,const char *nick); + +/* Bot Functions */ +void *tuveBotThread(void *threadid); +int tuveBotCMD(myConnections_t *userConnection,char *chan,char *data); +int tuveBotSetSong(char *chan,char *playTime,char *file,char *title); +int tuveBotNoSong(char *chan); + +/* Nick Functions */ +int tuveVerifyNick(char *nick); +myConnections_t *findNick(const char *); +int tuveWhois(myConnections_t *userConnection,char *nick); + +/* Server Functions */ +int tuveStatus(myConnections_t *userConnection); + +/* Database Functions */ +int dbInit(); +MYSQL_RES *dbQuery(const char *query);