--- /dev/null
+<?php
+/* NeonServ_QServer.class.php - NeonServ v5
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+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;
+ }
+}
+
+?>
--- /dev/null
+/* 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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");
+}