From: pk910 Date: Sun, 15 Jan 2012 12:34:53 +0000 (+0100) Subject: added QServer for external cache access X-Git-Tag: v5.3~50 X-Git-Url: http://git.pk910.de/?p=NeonServV5.git;a=commitdiff_plain;h=82f0cd1f3c2c0160944b0c627581f1994583e2b2 added QServer for external cache access --- diff --git a/Makefile.am b/Makefile.am index ea2f286..3789db9 100644 --- a/Makefile.am +++ b/Makefile.am @@ -134,7 +134,8 @@ neonserv_SOURCES = src/version.c \ src/cmd_neonserv_halfopall.c \ src/cmd_neonserv_dehalfopall.c \ src/cmd_funcmds.c \ - src/ConfigParser.c + src/ConfigParser.c \ + src/QServer.c neonserv_LDADD = $(MYSQL_LIBS) $(WINSOCK_LIBS) diff --git a/QServer/NeonServ_QServer.class.php b/QServer/NeonServ_QServer.class.php new file mode 100644 index 0000000..e3fb4b0 --- /dev/null +++ b/QServer/NeonServ_QServer.class.php @@ -0,0 +1,150 @@ +. + */ + +class NeonServ_QServer { + private $admin, $socket, $numeric; + + function NeonServ_QServer() { + $this->admin['pass']='*'; // QServer Passwort + $this->admin['host']='127.0.0.1'; + $this->admin['port']=7499; + } + + public function disconnect() { + fclose($this->socket); + $this->socket=false; + } + + public function connect() { + if($this->socket) { + $this->disconnect(); + } + $this->socket=@fsockopen($this->admin['host'], $this->admin['port'], $errno, $errstr, 3); + if ($this->socket) { + stream_set_timeout($this->socket, 2); + fputs($this->socket, "A ".$this->admin['pass']."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "A") + return true; + $this->disconnect(); //not logged in... + return false; + } + } + + public function connected() { + if($this->socket) return true; + return false; + } + + private function parseLine($line) { + $line = str_replace(array("\r", "\n"), array("", ""), $line); + $highExplode = explode(" :", $line, 2); + $tokens = explode(" ", $highExplode[0]); + if(count($highExplode) > 1) + $tokens[] = $highExplode[1]; + return $tokens; + } + + /* Get IRC User Information + * returns array: nick, ident, host, auth, realname + */ + public function getUser($nick) { + fputs($this->socket, "U ".$nick."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] != "U") return NULL; + if($data[1] == "0") return NULL; //User not found + $user = array(); + $user['nick'] = $data[2]; + $user['ident'] = $data[3]; + $user['host'] = $data[4]; + $user['auth'] = ($data[5] == "0" ? NULL : $data[5]); + $user['realname'] = $data[6]; + return $user; + } + + /* Get IRC Channel Information + * returns array: name, usercount, modes, topic + */ + public function getChannel($name) { + fputs($this->socket, "C ".$name."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] != "C") return NULL; + if($data[1] == "0") return NULL; //Channel not found + $chan = array(); + $chan['name'] = $data[2]; + $chan['usercount'] = $data[3]; + $chan['modes'] = implode(" ", array_slice($data, 4, (count($data) - 5))); + $chan['topic'] = $data[count($data)-1]; + return $chan; + } + + /* Get All IRC Channels + * returns array with subarrays: name, usercount, modes, topic + */ + public function getChannels() { + fputs($this->socket, "AC\n"); + $channels = array(); + while(true) { + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "E") return NULL; + if($data[0] == "ACE") break; + if($data[0] == "AC") { + $chan = array(); + $chan['name'] = $data[1]; + $chan['usercount'] = $data[2]; + $chan['modes'] = implode(" ", array_slice($data, 3, (count($data) - 4))); + $chan['topic'] = $data[count($data)-1]; + $channels[] = $chan; + } + } + return $channels; + } + + /* Get IRC Channel Users + * returns array with subarrays: nick, auth, flags + */ + public function getChannelUsers($name, $invisibles = false) { + fputs($this->socket, "ACU ".$name." ".($invisibles ? "1" : "0")."\n"); + $chanusers = array(); + while(true) { + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "E") return NULL; + if($data[0] == "ACUE") break; + if($data[0] == "ACU") { + $chanuser = array(); + $chanuser['nick'] = $data[1]; + $chanuser['auth'] = ($data[2] == "0" ? NULL : $data[2]); + $chanuser['flags'] = $data[3]; + $chanusers[] = $chanuser; + } + } + return $chanusers; + } + + /* send IRC RAW + * returns true / false + */ + public function sendRAW($class, $raw, $classIsNick = false) { + fputs($this->socket, "R ".($classIsNick ? "1" : "0")." ".$class." :".$raw."\n"); + $data = $this->parseLine(@fgets($this->socket)); + if($data[0] == "R" && $data[1] == "1") return true; + return false; + } +} + +?> diff --git a/neonserv.example.conf b/neonserv.example.conf index abb149d..b032164 100644 --- a/neonserv.example.conf +++ b/neonserv.example.conf @@ -12,4 +12,9 @@ "General" { "alertchan" = ""; "have_halfop" = 0; +}; +"QServer" { + "enabled" = 1; + "port" = 7499; + "pass" = "blaa"; }; \ No newline at end of file diff --git a/src/QServer.c b/src/QServer.c new file mode 100644 index 0000000..fd10beb --- /dev/null +++ b/src/QServer.c @@ -0,0 +1,421 @@ +/* QServer.c - NeonServ v5.3 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 . + */ + +#include "QServer.h" +#include "UserNode.h" +#include "ChanNode.h" +#include "ModeNode.h" +#include "ChanUser.h" +#include "ClientSocket.h" +#include "WHOHandler.h" +#include "ConfigParser.h" +#include "bots.h" + +#define QSERVER_TIMEOUT 30 +#define QSERVER_MAXCLIENTS 100 + +#define QSERVER_FLAG_DISCONNECT 0x01 +#define QSERVER_FLAG_AUTHED 0x02 +#define QSERVER_FLAG_IN_USE 0x04 + +struct QServerClient { + int sock; + unsigned int flags; + time_t lastmsg; + char buffer[MAXLEN]; + int bufferpos; + int references; + struct QServerClient *next; +}; + +static int server_sockfd = 0; +struct QServerClient *qserver_clients = NULL; +static int qserver_clientcount = 0; + +void qserver_init() { + if(get_int_field("QServer.enabled")) { + server_sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (server_sockfd < 0) + return; + struct sockaddr_in serv_addr; + bzero((char *) &serv_addr, sizeof(serv_addr)); + int portno = get_int_field("QServer.port"); + if(!portno) + portno = 7499; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = INADDR_ANY; + serv_addr.sin_port = htons(portno); + if (bind(server_sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + return; + listen(server_sockfd,5); + } +} + +static int qserver_write(struct QServerClient *client, char* msg, int len) { + if (!(client && !(client->flags & QSERVER_FLAG_DISCONNECT))) return 0; + if(!len) + len = strlen(msg); + #ifdef WIN32 + send(client->sock, msg, len, 0); + #else + write(client->sock, msg, len); + #endif + return 1; +} + +static void qserver_put(struct QServerClient *client, const char *text, ...) { + va_list arg_list; + char sendBuf[MAXLEN]; + int pos; + if (!(client && !(client->flags & QSERVER_FLAG_DISCONNECT))) return; + sendBuf[0] = '\0'; + va_start(arg_list, text); + pos = vsnprintf(sendBuf, MAXLEN - 2, text, arg_list); + va_end(arg_list); + if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2; + sendBuf[pos] = '\n'; + sendBuf[pos+1] = '\0'; + qserver_write(client, sendBuf, pos+1); +} + +static void qserver_parse_A(struct QServerClient *client, char **argv, int argc) { + if(client->flags & QSERVER_FLAG_AUTHED) { + qserver_put(client, "E :Already Authed"); + return; + } + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + if(strcmp(argv[0], get_string_field("QServer.pass"))) { + qserver_put(client, "E :Wrong Password"); + return; + } + client->flags |= QSERVER_FLAG_AUTHED; + client->lastmsg = time(0); + qserver_put(client, "A :Logged in"); +} + +#define QSERVER_COMMAND_HEADER {\ + if(!(client->flags & QSERVER_FLAG_AUTHED)) {\ + qserver_put(client, "E :Not Authed");\ + return;\ + }\ + client->lastmsg = time(0);\ +} + +static void qserver_parse_U(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_C(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc); +static void qserver_parse_R(struct QServerClient *client, char **argv, int argc); + +static void qserver_parse(struct QServerClient *client, char *line, int len) { + int argc = 0; + char *argv[MAXNUMPARAMS]; + while(*line) { + //skip leading spaces + while (*line == ' ') + *line++ = 0; + if (*line == ':') { + //the rest is a single parameter + argv[argc++] = line + 1; + break; + } + argv[argc++] = line; + if (argc >= MAXNUMPARAMS) + break; + while (*line != ' ' && *line) + line++; + } + if(!stricmp(argv[0], "A")) //AUTH + qserver_parse_A(client, argv+1, argc-1); + else if(!stricmp(argv[0], "U")) //get User + qserver_parse_U(client, argv+1, argc-1); + else if(!stricmp(argv[0], "C")) //get Channel + qserver_parse_C(client, argv+1, argc-1); + else if(!stricmp(argv[0], "AC")) //get All Channels + qserver_parse_AC(client, argv+1, argc-1); + else if(!stricmp(argv[0], "ACU")) //get All ChannelUsers + qserver_parse_ACU(client, argv+1, argc-1); + else if(!stricmp(argv[0], "R")) //RAW + qserver_parse_R(client, argv+1, argc-1); + else + qserver_put(client, "E :Unknown Command"); +} + +void qserver_loop() { + struct timeval tv; + struct QServerClient *client, *next, *prev = NULL; + int ret; + time_t now = time(0); + fd_set fds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fds); + ret = server_sockfd; + FD_SET(server_sockfd, &fds); + for (client = qserver_clients; client; client = next) { + next = client->next; + if((client->flags & (QSERVER_FLAG_DISCONNECT | QSERVER_FLAG_IN_USE)) == QSERVER_FLAG_DISCONNECT) { + close(client->sock); + if(prev) + prev->next = client->next; + else + qserver_clients = client->next; + qserver_clientcount--; + free(client); + continue; + } + prev = client; + if(client->flags & QSERVER_FLAG_DISCONNECT) continue; + if(now - client->lastmsg > QSERVER_TIMEOUT) { + qserver_put(client, "E :Timeout"); + client->flags |= QSERVER_FLAG_DISCONNECT; + continue; + } + FD_SET(client->sock, &fds); + if(client->sock > ret) + ret = client->sock; + } + ret = select(ret + 1, &fds, NULL, NULL, &tv); + if(ret == 0) { + return; + } + if(FD_ISSET(server_sockfd, &fds)) { + //new connection + struct sockaddr_in cli_addr; + socklen_t clilen; + if(qserver_clientcount >= QSERVER_MAXCLIENTS) { + qserver_put(client, "E :Maximum QServer Connections reached"); + close(client->sock); + } else { + client = malloc(sizeof(*client)); + clilen = sizeof(cli_addr); + client->sock = accept(server_sockfd, (struct sockaddr *) &cli_addr, &clilen); + client->flags = 0; + client->lastmsg = now; + client->bufferpos = 0; + client->references = 0; + client->next = qserver_clients; + qserver_clients = client; + qserver_clientcount++; + } + } + int bytes, i; + char buffer[MAXLEN]; + for (client = qserver_clients; client; client = next) { + next = client->next; + if(FD_ISSET(client->sock, &fds)) { + #ifdef WIN32 + bytes = recv(client->sock, buffer, sizeof(buffer), 0); + #else + bytes = read(client->sock, buffer, sizeof(buffer)); + #endif + if(bytes <= 0) { + client->flags |= QSERVER_FLAG_DISCONNECT; + continue; + } + for(i = 0; i < bytes; i++) { + if(client->bufferpos == MAXLEN-1) { + //buffer overflow + qserver_put(client, "E :Buffer Overflow"); + client->flags |= QSERVER_FLAG_DISCONNECT; + break; + } + if(buffer[i] == '\r') continue; + else if(buffer[i] == '\n') { + client->buffer[client->bufferpos] = '\0'; + qserver_parse(client, client->buffer, client->bufferpos); + client->bufferpos = 0; + } else { + client->buffer[client->bufferpos++] = buffer[i]; + } + } + } + } +} + +void qserver_free() { + struct QServerClient *client, *next; + for (client = qserver_clients; client; client = next) { + next = client->next; + close(client->sock); + free(client); + } + qserver_clients = NULL; + qserver_clientcount = 0; + close(server_sockfd); +} + +/* +* Command functions +*/ + +static USERAUTH_CALLBACK(qserver_parse_U_async); +static void qserver_parse_U(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct UserNode *cuser = getUserByNick(argv[0]); + if(!cuser) { + cuser = createTempUser(argv[0]); + if(!cuser) { + qserver_put(client, "U 0 :Unknown User"); + return; + } + cuser->flags |= USERFLAG_ISTMPUSER; + } + client->references++; + client->flags |= QSERVER_FLAG_IN_USE; + get_userauth(cuser, qserver_parse_U_async, client); +} + +static USERAUTH_CALLBACK(qserver_parse_U_async) { + struct QServerClient *qclient = data; + qclient->references--; + if(!qclient->references) + qclient->flags &= ~QSERVER_FLAG_IN_USE; + if(!user) { + qserver_put(qclient, "U 0 :Unknown User"); + return; + } + qserver_put(qclient, "U 1 %s %s %s %s :%s", user->nick, user->ident, user->host, ((user->flags & USERFLAG_ISAUTHED) ? user->auth : "0"), user->realname); +} + +static void qserver_parse_C(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct ChanNode *chan = getChanByName(argv[0]); + if(!chan) { + qserver_put(client, "C 0 :Unknown Channel"); + return; + } + char tmpStr[MAXLEN]; + getModeString(chan->modes, tmpStr); + qserver_put(client, "C 1 %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic); +} + +static void qserver_parse_AC(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + struct ChanNode *chan; + char tmpStr[MAXLEN]; + for(chan = getAllChans(NULL); chan; chan = getAllChans(chan)) { + getModeString(chan->modes, tmpStr); + qserver_put(client, "AC %s %d %s :%s", chan->name, chan->usercount, tmpStr, chan->topic); + } + qserver_put(client, "ACE"); //AllChannelsEnd +} + +static USERLIST_CALLBACK(qserver_parse_ACU_async); +static void qserver_parse_ACU(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(!argv) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct ChanNode *chan = getChanByName(argv[0]); + if(!chan) { + qserver_put(client, "ACUE 0 :Unknown Channel"); + return; + } + if(argc > 1 && !stricmp(argv[1], "1")) { + client->references++; + client->flags |= QSERVER_FLAG_IN_USE; + get_userlist_if_invisible(chan, qserver_parse_ACU_async, client); + return; + } + char tmpStr[6]; + int tmpStrPos; + struct ChanUser *chanuser; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + tmpStrPos = 0; + if(chanuser->flags & CHANUSERFLAG_OPPED) + tmpStr[tmpStrPos++] = '@'; + if(chanuser->flags & CHANUSERFLAG_HALFOPPED) + tmpStr[tmpStrPos++] = '%'; + if(chanuser->flags & CHANUSERFLAG_VOICED) + tmpStr[tmpStrPos++] = '+'; + if(chanuser->flags & CHANUSERFLAG_INVISIBLE) + tmpStr[tmpStrPos++] = '<'; + tmpStr[tmpStrPos] = '\0'; + qserver_put(client, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr); + } + qserver_put(client, "ACUE 1"); +} + +static USERLIST_CALLBACK(qserver_parse_ACU_async) { + struct QServerClient *qclient = data; + qclient->references--; + if(!qclient->references) + qclient->flags &= ~QSERVER_FLAG_IN_USE; + char tmpStr[6]; + int tmpStrPos; + struct ChanUser *chanuser; + for(chanuser = getChannelUsers(chan, NULL); chanuser; chanuser = getChannelUsers(chan, chanuser)) { + tmpStrPos = 0; + if(chanuser->flags & CHANUSERFLAG_OPPED) + tmpStr[tmpStrPos++] = '@'; + if(chanuser->flags & CHANUSERFLAG_HALFOPPED) + tmpStr[tmpStrPos++] = '%'; + if(chanuser->flags & CHANUSERFLAG_VOICED) + tmpStr[tmpStrPos++] = '+'; + if(chanuser->flags & CHANUSERFLAG_INVISIBLE) + tmpStr[tmpStrPos++] = '<'; + tmpStr[tmpStrPos] = '\0'; + qserver_put(qclient, "ACU %s %s %s", chanuser->user->nick, ((chanuser->user->flags & USERFLAG_ISAUTHED) ? chanuser->user->auth : "0"), tmpStr); + } + qserver_put(qclient, "ACUE 1"); +} + +static void qserver_parse_R(struct QServerClient *client, char **argv, int argc) { + QSERVER_COMMAND_HEADER; + if(argc < 3) { + qserver_put(client, "E :Missing Parameter"); + return; + } + struct ClientSocket *bot; + if(!strcmp(argv[0], "1")) { + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(!stricmp(bot->user->nick, argv[1])) break; + } + } else { + struct ClientSocket *low_bot; + int botid = resolve_botalias(argv[1]); + if(botid == -1) + botid = atoi(argv[1]); + for(bot = getBots(SOCKET_FLAG_READY, NULL); bot; bot = getBots(SOCKET_FLAG_READY, bot)) { + if(bot->botid == botid) { + if(bot->flags & SOCKET_FLAG_PREFERRED) break; + low_bot = bot; + } + } + if(!bot) + bot = low_bot; + } + if(!bot) { + qserver_put(client, "R 0 :Bot not found"); + return; + } + putsock(bot, "%s", argv[2]); + qserver_put(client, "R 1"); +} diff --git a/src/QServer.h b/src/QServer.h new file mode 100644 index 0000000..fc188c1 --- /dev/null +++ b/src/QServer.h @@ -0,0 +1,26 @@ +/* QServer.h - NeonServ v5.3 + * Copyright (C) 2011-2012 Philipp Kreil (pk910) + * + * 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 . + */ +#ifndef _QServer_h +#define _QServer_h + +#include "main.h" + +void qserver_init(); +void qserver_loop(); +void qserver_free(); + +#endif \ No newline at end of file diff --git a/src/main.c b/src/main.c index 107d50e..e8d366a 100644 --- a/src/main.c +++ b/src/main.c @@ -36,6 +36,7 @@ #include "commands.h" #include "ConfigParser.h" #include "ssl.h" +#include "QServer.h" time_t start_time; static int running, hard_restart; @@ -50,6 +51,7 @@ pthread_mutex_t whohandler_sync, whohandler_mass_sync; void cleanup() { free_sockets(); + qserver_free(); free_parser(); free_UserNode(); free_ChanNode(); @@ -156,6 +158,7 @@ main: register_commands(); init_bots(); init_DBHelper(); + qserver_init(); load_languages(); int update_minutes = get_int_field("statistics.frequency"); @@ -177,6 +180,7 @@ main: while(running) { timeq_tick(); loop_bots(); + qserver_loop(); queue_loop(); usleep(usleep_delay); } @@ -195,6 +199,7 @@ main: loop_bots(); clearTempUsers(); destroyEvents(); + qserver_loop(); queue_loop(); } #endif