12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751 |
- #include "httpserver.h"
- #include "ircclient.h"
- #include "connectiondata.h"
- #include "global.h"
- #include "version.h"
- #include <QRegularExpression>
- #include <QDirIterator>
- #include <QDateTime>
- #include <QHostAddress>
- #include <QTcpSocket>
- #include <QDebug>
- #include <QJsonDocument>
- #include <QJsonArray>
- #include <QJsonObject>
- #include <QFile>
- #include <QDir>
- const char CRITICAL_ERROR[] {"<title>Critical error</title><center><h1>NOT FOUND</H1><p>Maybe it's compile time error</p></center>"};
- HttpServer::HttpServer(const QString &address, quint16 port, const QString& logFolder,
- const QString& mainChannel, bool ajaxIsDisabled, QObject *parent) :
- QObject(parent),
- m_TcpServer(new QTcpServer),
- m_mainChannel(mainChannel),
- m_logFolder(logFolder),
- m_ajaxIsDisabled(ajaxIsDisabled)
- {
- if (not m_TcpServer->listen(QHostAddress(address), port)) {
- throw std::runtime_error("HttpServer not binded at " +
- address.toStdString() + " : " + QString::number(port).toStdString());
- }
- consoleLog(address + " : " + QString::number(port));
- if (m_ajaxIsDisabled) {
- consoleLog("JavaScript on webpages removed and AJAX disabled!");
- }
- connect (m_TcpServer, &QTcpServer::newConnection, this, &HttpServer::acceptor);
- }
- HttpServer::~HttpServer()
- {
- m_TcpServer->close();
- m_TcpServer->deleteLater();
- }
- QString HttpServer::convertToClickableLink(const QString &httpLine)
- {
- QString result;
- if (not httpLine.contains(QRegularExpression("http.?://"))) return result;
- QString displayedName {httpLine};
- displayedName.remove(QRegularExpression("http.?://(www\\.)?"));
- displayedName.remove(QRegularExpression("/$"));
- result = "<a href=\"" + httpLine + "\"> " + displayedName + " </a>";
- return result;
- }
- std::pair<QString, QString> HttpServer::splitUserNameAndMessage(const QString &rawLine)
- {
- std::pair<QString, QString> result;
- QString nick {rawLine};
- nick.remove(QRegularExpression("\\]\\s.*$"));
- nick.remove(QRegularExpression("^\\["));
- if (nick.isEmpty()) {
- return result;
- }
- nick = nick.toHtmlEscaped();
- // long nicks
- if (nick.size() > MAX_NICKNAME_LENGTH_WITHOUT_WBR) {
- int lastWbr = 0;
- for (int i = 0; i < nick.size(); i++) {
- if (i-lastWbr > MAX_NICKNAME_LENGTH_WITHOUT_WBR) {
- nick.insert(i, "<wbr>");
- lastWbr = i;
- }
- }
- }
- QString text {rawLine};
- text.remove(QRegularExpression("^\\[[^\\s]*\\]\\s"));
- if (text.isEmpty()) {
- return result;
- }
- text = text.toHtmlEscaped();
- // http links
- bool linksFound {false};
- while (QRegularExpression("(^|\\s)http.?://").match(text).hasMatch()) {
- if (not linksFound) linksFound = true;
- int pos = text.indexOf(QRegularExpression("(^|\\s)http.?://"));
- if (pos == -1) {
- consoleLog("Bug! HttpServer.cpp while (QRegularExpression(\"(^|\\s)http.?://\").match(text).hasMatch())");
- break;
- }
- QString rawLink {text};
- rawLink.remove(0, pos);
- if (rawLink.startsWith(' ')) {
- rawLink.remove(0,1);
- }
- int space = rawLink.indexOf(' ');
- if (space > 0) {
- rawLink.remove(space, rawLink.size()-space);
- }
- text.replace(rawLink, convertToClickableLink(rawLink));
- }
- // long lines
- int space = 0;
- bool nbTag = false; // For safe HTML tags like a ⁢ via <wbr>!
- bool isHref = false;
- for (int i = 0; i < text.size(); i++) {
- if (text[i] == ' ') {
- space = i;
- if (isHref) {
- isHref = false;
- }
- else {
- if (text.indexOf("href=\"http", i+1) == i+1) {
- isHref = true;
- }
- }
- }
- if (nbTag and text[i-1] == ';') {
- nbTag = false;
- }
- if (text.indexOf(QRegularExpression("(\\&|\\<|\\>|\\").*"), i) == i) {
- nbTag = true;
- }
- if (not isHref and i-space > MAX_MESSAGE_LENGTH_WITHOUT_WBR and not nbTag) {
- text.insert(i, "<wbr>");
- space = i;
- }
- }
- if (linksFound) text.replace(" </a>", "</a>"); // delete whitespace in links end
- result.first = nick;
- result.second = text;
- return result;
- }
- void HttpServer::consoleLog(const QString &message)
- {
- qInfo().noquote() << "[WEB_UI]" << message;
- }
- void HttpServer::debugLog(const QString &req)
- {
- QFile log(m_logFolder + "debug.log");
- if (log.open(QIODevice::WriteOnly | QIODevice::Append)) {
- log.write(QDateTime::currentDateTime().toString().toUtf8() + ":\n" + req.toUtf8() + "\n\n");
- log.close();
- }
- }
- void HttpServer::acceptor()
- {
- QTcpSocket* socket = m_TcpServer->nextPendingConnection();
- connect(socket, &QTcpSocket::readyRead, this, &HttpServer::reader);
- connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
- }
- void HttpServer::reader()
- {
- QTcpSocket* socket = static_cast<QTcpSocket*>(sender());
- QString request = socket->read(BUFFER_SIZE);
- if (not request.startsWith("GET") and not request.startsWith("HEAD")) {
- if (socket->isOpen()) {
- socket->write("Your request has been rejected!\n");
- socket->disconnectFromHost();
- }
- return;
- }
- bool isHeadRequest = false;
- if (request.startsWith("HEAD ")) {
- isHeadRequest = true;
- }
- QString urlPath = getRequestPath(request);
- // static files
- if (urlPath == "/favicon.ico") {
- QString eTag = global::getValue(request, "If-None-Match", global::eHttpHeader);
- if (eTag == HTTP_ACTUAL_ETAG) {
- if (socket->isOpen()) socket->write(HEADER_304.toUtf8());
- }
- else {
- QFile icon("://html/favicon.ico");
- if (icon.open(QIODevice::ReadOnly)) {
- QByteArray file = icon.readAll();
- icon.close();
- QString header = HEADER_ICO;
- replaceTag(header, "SIZE", QString::number(file.size()));
- if (socket->isOpen()) {
- socket->write(header.toUtf8());
- if (not isHeadRequest) socket->write(file);
- }
- }
- }
- }
- else if (urlPath == "/newmessage.mp3" and not m_ajaxIsDisabled) {
- QString eTag = global::getValue(request, "If-None-Match", global::eHttpHeader);
- if (eTag == HTTP_ACTUAL_ETAG) {
- if (socket->isOpen()) socket->write(HEADER_304.toUtf8());
- }
- else {
- QFile mp3("://html/newmessage.mp3");
- if (mp3.open(QIODevice::ReadOnly)) {
- QByteArray file = mp3.readAll();
- mp3.close();
- QString header = HEADER_MP3;
- replaceTag(header, "SIZE", QString::number(file.size()));
- if (socket->isOpen()) {
- socket->write(header.toUtf8());
- if (not isHeadRequest) socket->write(file);
- }
- }
- }
- }
- else if (urlPath == "/style.css") {
- QString eTag = global::getValue(request, "If-None-Match", global::eHttpHeader);
- if (eTag == HTTP_ACTUAL_ETAG) {
- if (socket->isOpen()) socket->write(HEADER_304.toUtf8());
- }
- else {
- QFile css("://html/style.css");
- if (css.open(QIODevice::ReadOnly)) {
- QByteArray file = css.readAll();
- css.close();
- QString header = HEADER_CSS;
- replaceTag(header, "SIZE", QString::number(file.size()));
- if (socket->isOpen()) {
- socket->write(header.toUtf8());
- if (not isHeadRequest) socket->write(file);
- }
- }
- }
- }
- else if (urlPath.endsWith(".svg")) {
- QString eTag = global::getValue(request, "If-None-Match", global::eHttpHeader);
- if (eTag == HTTP_ACTUAL_ETAG) {
- if (socket->isOpen()) socket->write(HEADER_304.toUtf8());
- }
- else {
- QFile svg("://html"+urlPath);
- if (svg.open(QIODevice::ReadOnly)) {
- QByteArray file = svg.readAll();
- svg.close();
- QString header = HEADER_SVG;
- replaceTag(header, "SIZE", QString::number(file.size()));
- if (socket->isOpen()) {
- socket->write(header.toUtf8());
- if (not isHeadRequest) socket->write(file);
- }
- }
- else {
- if (socket->isOpen()) {
- socket->write(HEADER_404.toUtf8());
- if (not isHeadRequest) writeErrorPage(socket);
- }
- }
- }
- }
- else if (urlPath == "/realtimechat.js" and not m_ajaxIsDisabled) {
- QFile js("://html/realtimechat.js");
- if (js.open(QIODevice::ReadOnly)) {
- QByteArray file = js.readAll();
- js.close();
- QString header = HEADER_JS;
- replaceTag(header, "SIZE", QString::number(file.size()));
- if (socket->isOpen()) {
- socket->write(header.toUtf8());
- if (not isHeadRequest) socket->write(file);
- }
- }
- }
- else if (urlPath.startsWith("/ajax/")) {
- if (m_ajaxIsDisabled) {
- writeErrorJson(socket, "AJAX disabled");
- } else {
- writeAjaxAnswer(socket, urlPath, isHeadRequest);
- }
- }
- else if (urlPath.startsWith("/realtimereadingchat/")) {
- if (m_ajaxIsDisabled) {
- writeErrorPage(socket, "DISABLED"
- "<p>No JS, no AJAX, no real time reading!</p>");
- } else {
- writeRealTimeChatPage(socket, urlPath, isHeadRequest);
- }
- }
- else if (urlPath.startsWith("/serverinformation/")) {
- writeAboutServerPage(socket, urlPath, isHeadRequest);
- }
- else {
- writeMainPage(socket, urlPath, isHeadRequest);
- }
- socket->disconnectFromHost();
- }
- void HttpServer::ircBotFirstInfo(QString server, QStringList channels)
- {
- for (const auto &c: channels) {
- if (c.isEmpty()) continue;
- m_servers[server][c] = QStringList();
- }
- }
- void HttpServer::ircUsersOnline(QString server, QString channel, QStringList users)
- {
- if (server.isEmpty()) return;
- if (channel.isEmpty()) return;
- QStringList sortedNicknames;
- QStringList ownersNicks;
- QStringList operNicks;
- QStringList halfopNicks;
- QStringList adminNicks;
- QStringList voicedNicks;
- QStringList plainNicks;
- for (auto rawOneNick: users) {
- rawOneNick = rawOneNick.toHtmlEscaped();
- if (rawOneNick.startsWith('~')) {
- ownersNicks.push_back(rawOneNick);
- }
- else if (rawOneNick.startsWith('@')) {
- operNicks.push_back(rawOneNick);
- }
- else if (rawOneNick.startsWith('%')) {
- halfopNicks.push_back(rawOneNick);
- }
- else if (rawOneNick.startsWith('&')) {
- adminNicks.push_back(rawOneNick);
- }
- else if (rawOneNick.startsWith('+')) {
- voicedNicks.push_back(rawOneNick);
- }
- else {
- plainNicks.push_back(rawOneNick);
- }
- }
- if (not ownersNicks.isEmpty()) {
- std::sort(ownersNicks.begin(), ownersNicks.end());
- sortedNicknames += ownersNicks;
- }
- if (not operNicks.isEmpty()) {
- std::sort(operNicks.begin(), operNicks.end());
- sortedNicknames += operNicks;
- }
- if (not halfopNicks.isEmpty()) {
- std::sort(halfopNicks.begin(), halfopNicks.end());
- sortedNicknames += halfopNicks;
- }
- if (not adminNicks.isEmpty()) {
- std::sort(adminNicks.begin(), adminNicks.end());
- sortedNicknames += adminNicks;
- }
- if (not voicedNicks.isEmpty()) {
- std::sort(voicedNicks.begin(), voicedNicks.end());
- sortedNicknames += voicedNicks;
- }
- if (not plainNicks.isEmpty()) {
- std::sort(plainNicks.begin(), plainNicks.end());
- sortedNicknames += plainNicks;
- }
- sortedNicknames.removeAll("");
- m_servers[server][channel] = sortedNicknames;
- }
- void HttpServer::ircChannelTopic(QString server, QString channel, QString topic)
- {
- m_channelsTopic[server][channel] = topic;
- }
- void HttpServer::ircServerOnline(QString server, quint8 status)
- {
- if (server.isEmpty()) return;
- bool online = status;
- m_serversOnline[server] = online;
- }
- void HttpServer::ircBotNick(QString server, QString nickname)
- {
- m_botNick[server] = nickname;
- }
- void HttpServer::ircMessageCache(QString server, QString channel, QString nick, QString text)
- {
- QString channelId {server+channel};
- if (not m_messageCache.contains(channelId)) return;
- // remove timed out session
- if (QDateTime::currentMSecsSinceEpoch() - MSECS_TO_AUTOREMOVE_MESSAGES_FROM_BUFFER >
- m_messageCache[channelId]->getLastPing())
- {
- consoleLog("Message caching disabled for "+server+"/#"+channel+". No active reader.");
- m_messageCache.remove(channelId);
- return;
- }
- else {
- auto processedMsg {splitUserNameAndMessage("["+nick+"] " + text)};
- m_messageCache[channelId]->saveNewMessage(processedMsg.first, processedMsg.second);
- }
- }
- QString HttpServer::getRequestPath(const QString &req)
- {
- if (req.isEmpty()) return QString();
- QString result(req);
- int begin = result.indexOf(' ');
- if (begin == -1) return QString();
- result.remove(0, begin+1);
- int space = result.indexOf(' ');
- int size = result.size();
- result.remove(space, size-space);
- result = QByteArray::fromPercentEncoding(result.toUtf8());
- return result;
- }
- QString HttpServer::getWordFromPath(const QString &path)
- {
- QString result {path};
- result.remove(QRegularExpression("\\?.*$")); // any actions like a ?toSearch=
- if (result.startsWith('/')) {
- result.remove(QRegularExpression("^/"));
- }
- result.remove(QRegularExpression("/.*$"));
- return result;
- }
- void HttpServer::writeErrorPage(QTcpSocket *socket, const QString& text)
- {
- if (socket->isOpen()) {
- socket->write(HEADER_404.toUtf8());
- socket->write("<title>404</title><center><h1>"+text.toUtf8()+"</H1></center>");
- }
- }
- void HttpServer::writeErrorJson(QTcpSocket * socket, const QString &text)
- {
- QString header = HEADER_JSON;
- QByteArray body {
- "{\"status\": false, \"message\": \"" + text.toUtf8() + "\"}"
- };
- replaceTag(header, "SIZE", QString::number(body.size()));
- socket->write(header.toUtf8());
- socket->write(body);
- }
- void HttpServer::removeBrakelineSymbols(QString &line)
- {
- line.remove('\r');
- line.remove('\n');
- line.remove('\t');
- }
- inline void HttpServer::replaceTag(QString &page, const QString &tag, const QString &payload)
- {
- page.replace("{{"+tag+"}}", payload);
- }
- void HttpServer::writeMainPage(QTcpSocket *socket, QString &urlPath, bool isHeadRequest)
- {
- auto renderStart = QDateTime::currentMSecsSinceEpoch();
- QString searchRequest;
- bool isRegexp {false};
- int specSymbol = urlPath.indexOf('?'); // any actions like a ?toSearch=
- if (specSymbol != -1) {
- searchRequest = global::getValue(urlPath, "toSearch", global::eForWeb);
- isRegexp = global::getValue(urlPath, "isRegexp", global::eForWeb) == "on";
- urlPath.remove(specSymbol, urlPath.size()-specSymbol);
- }
- if (urlPath == "/") {
- urlPath += m_mainChannel;
- }
- QFile main("://html/main.html");
- QString page;
- if (main.open(QIODevice::ReadOnly)) {
- page = main.readAll();
- main.close();
- }
- else {
- if (socket->isOpen()) {
- socket->write(HEADER_404.toUtf8());
- if (not isHeadRequest) socket->write(CRITICAL_ERROR);
- }
- }
- if (isRegexp) {
- page.replace("<input id=\"main_header__search_checkbox__button\" type=\"checkbox\" name=\"isRegexp\">",
- "<input id=\"main_header__search_checkbox__button\" type=\"checkbox\" name=\"isRegexp\" checked>");
- }
- QString server = getWordFromPath(urlPath);
- QDir fsPath(m_logFolder+server);
- if (not fsPath.exists()) {
- if (isHeadRequest) {
- if (socket->isOpen()) socket->write(HEADER_404.toUtf8());
- } else {
- writeErrorPage(socket, "REQUESTED SERVER LOG NOT EXIST");
- }
- return;
- }
- urlPath.remove(QRegularExpression("^.*/"+server));
- QString channel = getWordFromPath(urlPath);
- channel.remove(QRegularExpression("\\?.*$"));
- if (channel.isEmpty()){
- // First channel is main if not passed directly
- QDirIterator it(fsPath.path());
- while (it.hasNext()) {
- channel = it.next();
- if (channel.endsWith(".") or channel.endsWith("..")) continue; // QDir::NoDotAndNoDotDot not works!
- while(channel.contains('/')) {
- channel.remove(QRegularExpression("^.*/"));
- }
- break;
- }
- }
- if (not fsPath.cd(channel)) {
- if (isHeadRequest) {
- if (socket->isOpen()) socket->write(HEADER_404.toUtf8());
- } else {
- writeErrorPage(socket, "REQUESTED CHANNEL LOG NOT EXIST");
- }
- return;
- }
- QString originalServerName;
- for (const auto &s: m_servers) {
- if (global::toLowerAndNoSpaces(s.first) == server) {
- originalServerName = s.first;
- }
- }
- QString originalChannelName;
- for (const auto &server: m_servers) {
- for (const auto &channel_users: server.second) {
- if (global::toLowerAndNoSpaces(channel_users.first) == "#"+channel) {
- originalChannelName = global::toLowerAndNoSpaces(channel_users.first);
- }
- }
- }
- urlPath.remove(QRegularExpression("^.*/"+channel));
- QString year = getWordFromPath(urlPath);
- year.remove(QRegularExpression("\\?.*$"));
- QString month;
- QString day;
- if (not year.isEmpty() and fsPath.cd(year)) {
- urlPath.remove(QRegularExpression("^.*/"+year));
- month = getWordFromPath(urlPath);
- month.remove(QRegularExpression("\\?.*$"));
- if (not month.isEmpty() and fsPath.cd(month)) {
- if (urlPath.startsWith("/"+month+"/")) {
- urlPath.remove(0,1);
- int pos = urlPath.indexOf('/');
- if (pos != -1) {
- urlPath.remove(0,pos);
- day = getWordFromPath(urlPath);
- if (urlPath.endsWith(".txt")) {
- QFile plain(fsPath.path()+global::slash+day);
- if (plain.open(QIODevice::ReadOnly)) {
- QString header = HEADER_TEXT;
- QByteArray file = plain.readAll();
- plain.close();
- replaceTag(header, "SIZE", QString::number(file.size()));
- if (socket->isOpen()) {
- socket->write(header.toUtf8());
- if (not isHeadRequest) socket->write(file);
- }
- }
- else {
- if (isHeadRequest) {
- if (socket->isOpen()) socket->write(HEADER_404.toUtf8());
- } else {
- writeErrorPage(socket, "FILE OPENING FAILED");
- }
- }
- return;
- }
- else {
- if (not QFile::exists(fsPath.path()+global::slash+day+".txt")) {
- day.clear();
- }
- }
- }
- }
- }
- else { month.clear(); }
- }
- else { year.clear(); }
- //// Left menu compilation
- QString htmlServersSectionS;
- for (const auto &s: m_servers) {
- if (s.first.isEmpty()) continue; // empty server name?
- QString htmlServersSection = HTML_SERVER_SECTION;
- replaceTag(htmlServersSection, "ABOUT_SERVER", "/serverinformation/"+s.first);
- replaceTag(htmlServersSection, "SERVER_NAME", s.first);
- QString htmlChannelLineS;
- for (const auto &c: s.second) {
- QString htmlChannelLine;
- if (originalServerName == s.first and originalChannelName == c.first) {
- htmlChannelLine = HTML_SERVER_SECTION_CHANNEL_SELECTED;
- } else {
- htmlChannelLine = HTML_SERVER_SECTION_CHANNEL;
- }
- replaceTag(htmlChannelLine, "CHANNEL_NAME", c.first);
- QString channelNameForUrl {c.first};
- channelNameForUrl.remove('#');
- QString channelLink = "/" + global::toLowerAndNoSpaces(s.first) + "/" + channelNameForUrl;
- if (not year.isEmpty()) {
- channelLink += "/" + year;
- if (not month.isEmpty()) {
- channelLink += "/" + month;
- if (not day.isEmpty()) {
- channelLink += "/" + day;
- }
- }
- }
- replaceTag(htmlChannelLine, "CHANNEL_LINK", channelLink);
- htmlChannelLineS += htmlChannelLine;
- }
- replaceTag(htmlServersSection, "CHANNELS", htmlChannelLineS);
- bool online {false};
- for (const auto &srv: m_serversOnline) {
- if (srv.first == s.first) {
- online = srv.second;
- break;
- }
- }
- if (online) {
- replaceTag(htmlServersSection, "ONLINE_STATUS", HTML_SERVER_ONLINE_MARKER);
- } else {
- replaceTag(htmlServersSection, "ONLINE_STATUS", HTML_SERVER_OFFLINE_MARKER);
- }
- if (s.first == originalServerName) {
- htmlServersSectionS.push_front(htmlServersSection);
- } else {
- htmlServersSectionS += htmlServersSection;
- }
- }
- replaceTag(page, "SERVERS_SECTION", htmlServersSectionS);
- //// Main section header compilation
- QString& topic = m_channelsTopic[originalServerName][originalChannelName];
- topic = topic.replace('\"', """);
- QString titlePostfix = " | IRCaBot";
- if (not topic.isEmpty()) {
- titlePostfix.push_front(" | " + topic);
- }
- if (m_servers.size() > 1) {
- replaceTag(page, "PAGE_TITLE", originalChannelName + " ("+originalServerName+")" + titlePostfix);
- } else {
- replaceTag(page, "PAGE_TITLE", originalChannelName + titlePostfix);
- }
- replaceTag(page, "CHANNEL_TOPIC", topic);
- replaceTag(page, "MAIN_HEADER", originalChannelName);
- if (m_ajaxIsDisabled) {
- page.remove("<a href=\"{{REALTIME_LINK}}\" title=\"{{AIRPLAIN_TITLE}}\" class=\"main_header__title_airplaine\"></a>");
- }
- else {
- replaceTag(page, "REALTIME_LINK", "/realtimereadingchat/"+server+"/"+channel);
- replaceTag(page, "AIRPLAIN_TITLE", "Read in real time");
- }
- QString middlePath = "<a style=\"text-decoration: none\" href=\"/"+server+"/"+channel+"\">" + "/" + "</a>";
- if (not year.isEmpty()) {
- middlePath += "<a style=\"text-decoration: none\" href=\"/"+server+"/"+channel+"/"+year+"\">" + year + "</a>";
- if (not month.isEmpty()) {
- middlePath += "/<a style=\"text-decoration: none\" href=\"/"+server+"/"+channel+"/"+year+"/"+month+"\">" + month + "</a>";
- if (not day.isEmpty()) {
- middlePath += "/" + day;
- }
- }
- }
- QString arrows = HTML_PAYLOAD_ADDITIONAL_ARROWS;
- QString currentYear {QDateTime::currentDateTime().toString("yyyy")};
- QString currentMonth {QDateTime::currentDateTime().toString("MM")};
- QString currentDay {QDateTime::currentDateTime().toString("dd")};
- replaceTag(arrows, "CURRENT_DATA_LOG", "/"+server+"/"+channel+"/"+
- currentYear+"/"+currentMonth+"/"+currentDay);
- replaceTag(page, "ADDITIONAL_BUTTON", arrows);
- int currentOnline = 0;
- QString onlineUserS;
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+m_botNick[originalServerName]+"$").match(user).hasMatch()) {
- continue;
- }
- QString onlineUser = HTML_ONLINE_POINT;
- replaceTag(onlineUser, "NICKNAME", user);
- onlineUserS += onlineUser;
- currentOnline++;
- }
- replaceTag(page, "ONLINE", QString::number(currentOnline));
- replaceTag(page, "ONLINE_LIST", onlineUserS);
- if (not searchRequest.isEmpty()) {
- page.replace("<input class=\"main_header__search_input\" type=\"search\" name=\"toSearch\" placeholder=\"{{SEARCH_PLACEHOLDER}}\">",
- "<input class=\"main_header__search_input\" type=\"search\" name=\"toSearch\" value=\"" + searchRequest.toHtmlEscaped() + "\">");
- } else if (middlePath == "/") {
- replaceTag(page, "SEARCH_PLACEHOLDER", originalChannelName);
- } else if (month.isEmpty()) {
- replaceTag(page, "SEARCH_PLACEHOLDER", originalChannelName + "/" + year);
- } else {
- replaceTag(page, "SEARCH_PLACEHOLDER", originalChannelName + "/" + year + "/" + month);
- }
- //// Main section body compilation
- QString payloadBlock;
- // Search request
- if (not searchRequest.isEmpty()) {
- uint counter = 0;
- QRegularExpression userRgx(searchRequest, QRegularExpression::CaseInsensitiveOption);
- bool rgxIsValid = false;
- if (isRegexp and userRgx.isValid()) {
- rgxIsValid = true;
- }
- consoleLog("Search request (" + server + "): " + searchRequest);
- if (not day.isEmpty()) {
- middlePath.remove(QRegularExpression("/[0-9]{2}$"));
- }
- QStringList paths;
- QDirIterator it(fsPath.path());
- while (it.hasNext()) {
- QString currentPath = it.next();
- if (currentPath.endsWith(".") or currentPath.endsWith("..")) continue;
- QString logFolder = m_logFolder;
- #ifdef WIN32
- logFolder.replace('\\', '/');
- #endif
- QString server {currentPath}; // Folder wich is not server folder is ignored
- server.remove(QRegularExpression("^"+logFolder));
- server.remove(QRegularExpression("/.*$"));
- bool serverIsOk = false;
- for (const auto &srv: m_servers) {
- if (global::toLowerAndNoSpaces(srv.first) == server) {
- serverIsOk = true;
- break;
- }
- }
- if (not serverIsOk) continue;
- QString currentChannel {currentPath}; // Folder wich is not channel folder is ignored
- currentChannel.remove(QRegularExpression("^"+logFolder+"[^/]*/"));
- currentChannel.remove(QRegularExpression("/.*$"));
- bool channelIsOk = false; // Канал явно указан в конфиге
- for (const auto &ch: m_servers[originalServerName]) {
- QString searchChan {ch.first};
- searchChan.remove('#');
- if (searchChan == currentChannel) {
- channelIsOk = true;
- break;
- }
- }
- if (not channelIsOk) continue;
- paths.push_back(currentPath);
- }
- if (paths.isEmpty()) {
- payloadBlock = HTML_PAYLOAD_ERROR;
- replaceTag(payloadBlock, "ERROR_TITLE", "Not found");
- replaceTag(payloadBlock, "ERROR_TEXT", "");
- }
- else {
- std::map<QString, QStringList> matchedPathsAndMessages;
- if (not month.isEmpty()) {
- for (const auto& path: paths) {
- if (not QRegularExpression("^.*[0-9]{2}\\.txt$").match(path).hasMatch()) continue;
- QFile file(path);
- if (not file.open(QIODevice::ReadOnly)) {
- consoleLog("Error! I can't open log file " + fsPath.path());
- continue;
- }
- QString buffer {file.readLine()};
- while (not buffer.isEmpty()) {
- removeBrakelineSymbols(buffer);
- bool finded = false;
- if (rgxIsValid) {
- if (QRegularExpression(searchRequest, QRegularExpression::CaseInsensitiveOption).match(buffer).hasMatch()) {
- finded = true;
- }
- } else {
- if (buffer.contains(searchRequest, Qt::CaseInsensitive)) {
- finded = true;
- }
- }
- if (finded) {
- std::pair<QString, QString> rawMessage = splitUserNameAndMessage(buffer);
- if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) {
- buffer = file.readLine();
- continue;
- }
- counter++;
- QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE;
- if (rawMessage.second == global::BLINDED_MESSAGE_MERKER) {
- message.replace("class=\"main_payload__chat\"",
- "class=\"main_payload__chat\" style=\"opacity: .5\"");
- }
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+rawMessage.first+"$").match(user).hasMatch()) {
- message.replace("class=\"main_payload__chat_username\"",
- "class=\"main_payload__chat_username\" style=\"color: green\"");
- break;
- }
- }
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- matchedPathsAndMessages[path].push_back(message);
- }
- buffer = file.readLine();
- }
- file.close();
- }
- }
- else if (month.isEmpty() and not year.isEmpty()){
- for (const auto &p: paths) {
- QStringList slavePaths;
- QDirIterator it(p);
- while (it.hasNext()) {
- QString fileName = it.next();
- if (fileName.endsWith(".") or fileName.endsWith("..")) continue;
- if (not QRegularExpression("\\.txt$").match(fileName).hasMatch()) continue;
- slavePaths.push_back(fileName);
- }
- for (const auto &path: slavePaths) {
- if (not QRegularExpression("^.*[0-9]{2}\\.txt$").match(path).hasMatch()) continue;
- QFile file(path);
- if (not file.open(QIODevice::ReadOnly)) {
- consoleLog("Error! I can't open log file " + fsPath.path());
- continue;
- }
- QString buffer {file.readLine()};
- while (not buffer.isEmpty()) {
- removeBrakelineSymbols(buffer);
- bool finded = false;
- if (rgxIsValid) {
- if (QRegularExpression(searchRequest, QRegularExpression::CaseInsensitiveOption).match(buffer).hasMatch()) {
- finded = true;
- }
- } else {
- if (buffer.contains(searchRequest, Qt::CaseInsensitive)) {
- finded = true;
- }
- }
- if (finded) {
- std::pair<QString, QString> rawMessage = splitUserNameAndMessage(buffer);
- if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) {
- buffer = file.readLine();
- continue;
- }
- counter++;
- QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE;
- if (rawMessage.second == global::BLINDED_MESSAGE_MERKER) {
- message.replace("class=\"main_payload__chat\"",
- "class=\"main_payload__chat\" style=\"opacity: .5\"");
- }
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^^(.*;|~|@|\\&|\\+)?"+rawMessage.first+"$").match(user).hasMatch()) {
- message.replace("class=\"main_payload__chat_username\"",
- "class=\"main_payload__chat_username\" style=\"color: green\"");
- break;
- }
- }
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- matchedPathsAndMessages[path].push_back(message);
- }
- buffer = file.readLine();
- }
- file.close();
- }
- }
- }
- else { // root directory
- QStringList yearPaths;
- for (const auto& p: paths) {
- if (not QRegularExpression("/2[0-9]{3}$").match(p).hasMatch()) continue;
- yearPaths.push_back(p);
- }
- /* If you are reading this code, maybe you are crying now.
- * Sorry me, man (woman/fagot/etc). Maybe I'll refactor this hell in the future.
- * acetone.
- */
- QStringList fileNameS;
- for (const auto& p: yearPaths) {
- QStringList slavePaths;
- QDirIterator it(p);
- while (it.hasNext()) {
- QString folderName = it.next();
- if (folderName.endsWith(".") or folderName.endsWith("..")) continue;
- if (not QRegularExpression("/[0-9]{2}$").match(folderName).hasMatch()) continue;
- slavePaths.push_back(folderName);
- }
- for (const auto &path: slavePaths) {
- QDirIterator itMonth(path);
- while (itMonth.hasNext()) {
- QString fileName = itMonth.next();
- if (fileName.endsWith(".") or fileName.endsWith("..")) continue;
- if (not QRegularExpression("^.*[0-9]{2}\\.txt$").match(fileName).hasMatch()) continue;
- fileNameS.push_back(fileName);
- }
- }
- }
- for (const auto& path: fileNameS) {
- QFile file(path);
- if (not file.open(QIODevice::ReadOnly)) {
- consoleLog("Error! I can't open log file " + fsPath.path());
- continue;
- }
- QString buffer {file.readLine()};
- while (not buffer.isEmpty()) {
- removeBrakelineSymbols(buffer);
- bool finded = false;
- if (rgxIsValid) {
- if (QRegularExpression(searchRequest, QRegularExpression::CaseInsensitiveOption).match(buffer).hasMatch()) {
- finded = true;
- }
- } else {
- if (buffer.contains(searchRequest, Qt::CaseInsensitive)) {
- finded = true;
- }
- }
- if (finded) {
- std::pair<QString, QString> rawMessage = splitUserNameAndMessage(buffer);
- if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) {
- buffer = file.readLine();
- continue;
- }
- counter++;
- QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE;
- if (rawMessage.second == global::BLINDED_MESSAGE_MERKER) {
- message.replace("class=\"main_payload__chat\"",
- "class=\"main_payload__chat\" style=\"opacity: .5\"");
- }
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^^(.*;|~|@|\\&|\\+)?"+rawMessage.first+"$").match(user).hasMatch()) {
- message.replace("class=\"main_payload__chat_username\"",
- "class=\"main_payload__chat_username\" style=\"color: green\"");
- break;
- }
- }
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- matchedPathsAndMessages[path].push_back(message);
- }
- buffer = file.readLine();
- }
- file.close();
- }
- }
- if (matchedPathsAndMessages.empty()) {
- payloadBlock = HTML_PAYLOAD_ERROR;
- replaceTag(payloadBlock, "ERROR_TITLE", "Not found");
- replaceTag(payloadBlock, "ERROR_TEXT", "");
- }
- else {
- QStringList findedPaths;
- for (const auto& fp: matchedPathsAndMessages) {
- findedPaths << fp.first;
- }
- std::sort(findedPaths.begin(), findedPaths.end());
- for (auto& link: findedPaths) {
- QString logFolder {m_logFolder};
- logFolder.remove(QRegularExpression(".$"));
- QStringList& messages = matchedPathsAndMessages[link];
- link.remove(logFolder);
- link.remove(QRegularExpression("\\.txt$"));
- QString finded = HTML_PAYLOAD_LIST_POINT_MESSAGE;
- finded.replace("class=\"main_payload__block\"", "class=\"main_payload__block\" style=\"background: #b6c7d6\"");
- replaceTag(finded, "POINT_LINK", link);
- link.remove(QRegularExpression("^.*"+channel));
- replaceTag(finded, "POINT_CONTENT", link);
- payloadBlock += finded;
- for(const auto& m: messages) {
- payloadBlock += m;
- }
- payloadBlock += " \n";
- }
- }
- }
- middlePath += " " + searchRequest + " ";
- if (rgxIsValid) middlePath += "rgx";
- middlePath += "(" + QString::number(counter) + ")";
- }
- // Plain log explorer
- else {
- if (year.isEmpty()) { // /
- QStringList folderNameS;
- QDirIterator it(fsPath.path());
- while (it.hasNext()) {
- QString folderName = it.next();
- if (folderName.endsWith(".") or folderName.endsWith("..")) continue;
- while(folderName.contains('/')) {
- folderName.remove(QRegularExpression("^.*/"));
- }
- folderName.remove(QRegularExpression("\\.txt$"));
- folderNameS << folderName;
- }
- if (not folderNameS.isEmpty()) {
- std::sort(folderNameS.begin(), folderNameS.end());
- for (const auto &f: folderNameS) {
- QString onePoint = HTML_PAYLOAD_LIST_POINT_FOLDER;
- replaceTag(onePoint, "POINT_CONTENT", f);
- replaceTag(onePoint, "POINT_LINK", "/"+server+"/"+channel+"/"+f);
- payloadBlock += onePoint;
- }
- }
- }
- else if (not year.isEmpty() and month.isEmpty()) { // /YYYY
- QStringList folderNameS;
- QDirIterator it(fsPath.path());
- while (it.hasNext()) {
- QString folderName = it.next();
- if (folderName.endsWith(".") or folderName.endsWith("..")) continue;
- while(folderName.contains('/')) {
- folderName.remove(QRegularExpression("^.*/"));
- }
- folderNameS << folderName;
- }
- if (not folderNameS.isEmpty()) {
- std::sort(folderNameS.begin(), folderNameS.end());
- for (const auto &f: folderNameS) {
- QString onePoint = HTML_PAYLOAD_LIST_POINT_FOLDER;
- bool yearIsOk {false};
- bool monthIsOk {false};
- QDate dateOfMonth (year.toInt(&yearIsOk), f.toInt(&monthIsOk), 1);
- QString nameOfMonth;
- if (yearIsOk and monthIsOk) {
- nameOfMonth = QLocale().standaloneMonthName(dateOfMonth.month(), QLocale::ShortFormat);
- }
- QDirIterator dayLogs(fsPath.path() + global::slash + f);
- int8_t dayLogsCounter {0};
- while (dayLogs.hasNext()) {
- QString dayLogFile = dayLogs.next();
- if (dayLogFile.endsWith(".") or dayLogFile.endsWith("..")) continue;
- dayLogsCounter++;
- }
- QString filesLabel;
- dayLogsCounter == 1 ? filesLabel = "day" : filesLabel = "days";
- replaceTag(onePoint, "POINT_CONTENT", "<span style=\"font-weight: bold\">" + f + "</span> (" + nameOfMonth + ") " + QString::number(dayLogsCounter) + " " + filesLabel);
- replaceTag(onePoint, "POINT_LINK", "/"+server+"/"+channel+"/"+year+"/"+f);
- payloadBlock += onePoint;
- }
- }
- }
- else if (not month.isEmpty() and day.isEmpty()) { // /YYYY/MM
- QStringList fileNameS;
- QDirIterator it(fsPath.path());
- while (it.hasNext()) {
- QString fileName = it.next();
- if (fileName.endsWith(".") or fileName.endsWith("..")) continue; // QDir::NoDotAndNoDotDot not works!
- while(fileName.contains('/')) {
- fileName.remove(QRegularExpression("^.*/"));
- }
- fileName.remove(QRegularExpression("\\.txt$"));
- fileNameS << fileName;
- }
- if (not fileNameS.isEmpty()) {
- std::sort(fileNameS.begin(), fileNameS.end());
- for (const auto &a: fileNameS) {
- QString onePoint = HTML_PAYLOAD_LIST_POINT_MESSAGE;
- bool yearIsOk {false};
- bool monthIsOk {false};
- bool dayIsOk {false};
- QDate dateOfDay (year.toInt(&yearIsOk), month.toInt(&monthIsOk), a.toInt(&dayIsOk));
- QString nameOfDay;
- if (yearIsOk and monthIsOk and dayIsOk) {
- nameOfDay = QLocale().standaloneDayName(dateOfDay.dayOfWeek(), QLocale::ShortFormat);
- }
- auto logFileSizeBytes = QFile(fsPath.path() + global::slash + a +".txt").size();
- auto logFileSizeinKb = logFileSizeBytes/1000;
- QString logFileSizeString;
- if (logFileSizeinKb != 0) {
- logFileSizeString = QString::number(logFileSizeinKb)+"."+
- QString::number((logFileSizeBytes - logFileSizeinKb*1000)/10)+
- " KB";
- } else {
- logFileSizeString = QString::number(logFileSizeBytes)+" B";
- }
- replaceTag(onePoint, "POINT_CONTENT", "<span style=\"font-weight: bold\">" + a + "</span> (" + nameOfDay + ") " + logFileSizeString);
- replaceTag(onePoint, "POINT_LINK", "/"+server+"/"+channel+"/"+year+"/"+month+"/"+a);
- payloadBlock += onePoint;
- }
- }
- }
- else if (not day.isEmpty()) { // /YYYY/MM/dd
- QFile file(fsPath.path()+global::slash+day+".txt");
- if (not file.open(QIODevice::ReadOnly)) {
- consoleLog("Error! I can't open log file " + fsPath.path());
- payloadBlock = HTML_PAYLOAD_ERROR;
- replaceTag(payloadBlock, "ERROR_TITLE", "Internal error");
- replaceTag(payloadBlock, "ERROR_TEXT", "Requested log file openning failed");
- }
- else {
- QString buffer = file.readLine();
- while (not buffer.isEmpty()) {
- removeBrakelineSymbols(buffer);
- std::pair<QString, QString> rawMessage = splitUserNameAndMessage(buffer);
- if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) {
- buffer = file.readLine();
- continue;
- }
- QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE;
- if (rawMessage.second == global::BLINDED_MESSAGE_MERKER) {
- message.replace("class=\"main_payload__chat\"",
- "class=\"main_payload__chat\" style=\"opacity: .5\"");
- }
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+rawMessage.first+"$").match(user).hasMatch()) {
- message.replace("class=\"main_payload__chat_username\"",
- "class=\"main_payload__chat_username\" style=\"color: green\"");
- break;
- }
- }
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- payloadBlock += message;
- buffer = file.readLine();
- }
- file.close();
- }
- }
- if (payloadBlock.isEmpty()) {
- payloadBlock = HTML_PAYLOAD_ERROR;
- replaceTag(payloadBlock, "ERROR_TITLE", "Empty");
- replaceTag(payloadBlock, "ERROR_TEXT", "No logs found for this channel");
- }
- }
- replaceTag(page, "MIDDLE_PATH", middlePath);
- replaceTag(page, "PAYLOAD_BLOCK", payloadBlock);
- //// Footer
- replaceTag(page, "VERSION", IRCABOT_VERSION);
- replaceTag(page, "COPYRIGHT_YEAR", COPYRIGHT_YEAR);
- replaceTag(page, "RENDERING_TIMER", QString::number(QDateTime::currentMSecsSinceEpoch() - renderStart));
- QString mainHeader = HEADER_HTML;
- replaceTag(mainHeader, "SIZE", QString::number(QByteArray(page.toUtf8()).size()));
- if (socket->isOpen()) {
- socket->write(mainHeader.toUtf8());
- if (not isHeadRequest) socket->write(page.toUtf8());
- }
- }
- void HttpServer::writeRealTimeChatPage(QTcpSocket *socket, QString &urlPath, bool isHeadRequest)
- {
- if (isHeadRequest) {
- QString header = HEADER_HTML;
- replaceTag(header, "SIZE", "0");
- socket->write(header.toUtf8());
- return;
- }
- auto renderStart = QDateTime::currentMSecsSinceEpoch();
- urlPath.remove(QRegularExpression("^/realtimereadingchat/"));
- QString server = getWordFromPath(urlPath);
- if (server.isEmpty()) {
- writeErrorPage(socket, "SERVER VALUE IS MISSING");
- return;
- }
- urlPath.remove(QRegularExpression("^"+server));
- QString channel = getWordFromPath(urlPath);
- if (channel.isEmpty()) {
- writeErrorPage(socket, "CHANNEL VALUE IS MISSING");
- return;
- }
- QString originalServerName;
- for (const auto &s: m_servers) {
- if (global::toLowerAndNoSpaces(s.first) == server) {
- originalServerName = s.first;
- }
- }
- if (originalServerName.isEmpty()) {
- writeErrorPage(socket, "REQUESTED SERVER NOT EXIST");
- return;
- }
- QString originalChannelName;
- for (const auto &server: m_servers) {
- for (const auto &channel_users: server.second) {
- if (global::toLowerAndNoSpaces(channel_users.first) == "#"+channel) {
- originalChannelName = global::toLowerAndNoSpaces(channel_users.first);
- }
- }
- }
- if (originalChannelName.isEmpty()) {
- writeErrorPage(socket, "REQUESTED CHANNEL NOT EXIST");
- return;
- }
- QFile main("://html/main.html");
- QString page;
- if (main.open(QIODevice::ReadOnly)) {
- page = main.readAll();
- main.close();
- }
- else {
- if (socket->isOpen()) {
- socket->write(HEADER_404.toUtf8());
- if (not isHeadRequest) socket->write(CRITICAL_ERROR);
- }
- }
- QString year {QDateTime::currentDateTime().toString("yyyy")};
- QString month {QDateTime::currentDateTime().toString("MM")};
- QString day {QDateTime::currentDateTime().toString("dd")};
- //// Left menu compilation
- QString htmlServersSectionS;
- for (const auto &s: m_servers) {
- if (s.first.isEmpty()) continue; // empty server name?
- QString htmlServersSection = HTML_SERVER_SECTION;
- replaceTag(htmlServersSection, "ABOUT_SERVER", "/serverinformation/"+server);
- replaceTag(htmlServersSection, "SERVER_NAME", s.first);
- if (s.first == originalServerName) {
- htmlServersSection.replace("<span style=\"font-size: 17px;\">{{ONLINE_STATUS}}",
- "<span style=\"font-size: 17px;\" id=\"serverStatus\">{{ONLINE_STATUS}}");
- }
- QString htmlChannelLineS;
- for (const auto &c: s.second) {
- QString htmlChannelLine;
- if (originalServerName == s.first and originalChannelName == c.first) {
- htmlChannelLine = HTML_SERVER_SECTION_CHANNEL_SELECTED;
- } else {
- htmlChannelLine = HTML_SERVER_SECTION_CHANNEL;
- }
- replaceTag(htmlChannelLine, "CHANNEL_NAME", c.first);
- QString channelNameForUrl {c.first};
- channelNameForUrl.remove('#');
- QString channelLink = "/realtimereadingchat/" + global::toLowerAndNoSpaces(s.first) + "/" + channelNameForUrl;
- replaceTag(htmlChannelLine, "CHANNEL_LINK", channelLink);
- htmlChannelLineS += htmlChannelLine;
- }
- replaceTag(htmlServersSection, "CHANNELS", htmlChannelLineS);
- bool online {false};
- for (const auto &srv: m_serversOnline) {
- if (srv.first == s.first) {
- online = srv.second;
- break;
- }
- }
- if (online) {
- replaceTag(htmlServersSection, "ONLINE_STATUS", HTML_SERVER_ONLINE_MARKER);
- } else {
- replaceTag(htmlServersSection, "ONLINE_STATUS", HTML_SERVER_OFFLINE_MARKER);
- }
- if (s.first == originalServerName) {
- htmlServersSectionS.push_front(htmlServersSection);
- } else {
- htmlServersSectionS += htmlServersSection;
- }
- }
- replaceTag(page, "SERVERS_SECTION", htmlServersSectionS);
- //// Main section header compilation
- QString& topic = m_channelsTopic[originalServerName][originalChannelName];
- topic = topic.replace('\"', """);
- QString titlePostfix = " | IRCaBot";
- if (not topic.isEmpty()) {
- titlePostfix.push_front(" | " + topic);
- }
- if (m_servers.size() > 1) {
- replaceTag(page, "PAGE_TITLE", originalChannelName + " ("+originalServerName+") [real time]" + titlePostfix);
- } else {
- replaceTag(page, "PAGE_TITLE", originalChannelName + " [real time]" + titlePostfix);
- }
- replaceTag(page, "CHANNEL_TOPIC", topic);
- replaceTag(page, "MAIN_HEADER", originalChannelName);
- replaceTag(page, "ADDITIONAL_BUTTON", HTML_PAYLOAD_ADDITIONAL_NOTIFY);
- replaceTag(page, "REALTIME_LINK", "/"+server+"/"+channel+"/"+year+"/"+month+"/"+day);
- replaceTag(page, "AIRPLAIN_TITLE", "Back to plain text log");
- page.replace("class=\"main_header__title_airplaine\"", "class=\"main_header__title_airplaine\" style=\"transform: scale(-1,1)\"");
- int currentOnline = 0;
- QString onlineUserS;
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+m_botNick[originalServerName]+"$").match(user).hasMatch()) {
- continue;
- }
- QString onlineUser = HTML_ONLINE_POINT;
- replaceTag(onlineUser, "NICKNAME", user);
- onlineUserS += onlineUser;
- currentOnline++;
- }
- page.replace("{{ONLINE}}", "<span id=\"online\">{{ONLINE}}</span>");
- replaceTag(page, "ONLINE", QString::number(currentOnline));
- page.replace("<div class=\"main_middle__online_list\">", "<div id=\"onlineList\" class=\"main_middle__online_list\">");
- replaceTag(page, "ONLINE_LIST", onlineUserS);
- page.replace("class=\"main_header__search_form\"", "action=\"/"+server+"/"+channel+"\" class=\"main_header__search_form\"");
- replaceTag(page, "SEARCH_PLACEHOLDER", originalChannelName);
- page.replace("<div class=\"main_middle__path\">", "<div class=\"main_middle__path\" id=\"path\">");
- replaceTag(page, "MIDDLE_PATH", "");
- //// Payload
- page.replace("<div class=\"main_payload\">", "<div class=\"main_payload\" id=\"payload\">");
- bool logsExisted {false};
- QFile file;
- QDir fsPath(m_logFolder+server+global::slash+channel);
- if (fsPath.cd(year)) {
- if (fsPath.cd(month)) {
- file.setFileName(fsPath.path()+global::slash+day+".txt");
- if (file.open(QIODevice::ReadOnly)) {
- logsExisted = true;
- }
- }
- }
- QString payloadBlock;
- if (logsExisted) {
- QString buffer = file.readLine();
- while (not buffer.isEmpty()) {
- removeBrakelineSymbols(buffer);
- std::pair<QString, QString> rawMessage = splitUserNameAndMessage(buffer);
- if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) {
- buffer = file.readLine();
- continue;
- }
- QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE;
- if (rawMessage.second == global::BLINDED_MESSAGE_MERKER) {
- message.replace("class=\"main_payload__chat\"",
- "class=\"main_payload__chat\" style=\"opacity: .5\"");
- }
- message.replace("class=\"main_payload__chat_username\"",
- "class=\"main_payload__chat_username\" style=\"color: #e34f10\"");
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- payloadBlock += message;
- buffer = file.readLine();
- }
- file.close();
- }
- QString hr = HTML_PAYLOAD_LIST_CHAT_MESSAGE;
- hr.replace("class=\"main_payload__chat_mail\"", "id=\"hr\" class=\"main_payload__chat_mail\"");
- hr.replace("class=\"main_payload__chat_username\"",
- "class=\"main_payload__chat_username\" style=\"color: white; text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;\"");
- replaceTag(hr, "USERNAME", "IRCaBot");
- replaceTag(hr, "MESSAGE_TEXT", "<b>New messages won't show without JavaScript.</b><br>"
- "My JS code is small and simple. Check it at "
- "<a href=\"/realtimechat.js\">/realtimechat.js</a> and come back "
- "with enabled!");
- payloadBlock += hr;
- replaceTag(page, "PAYLOAD_BLOCK", payloadBlock);
- //// Footer
- replaceTag(page, "VERSION", IRCABOT_VERSION);
- replaceTag(page, "COPYRIGHT_YEAR", COPYRIGHT_YEAR);
- //// Finish
- page.replace("</body>", " <div id=\"LMId\" style=\"display: none\">" + QString::number(QDateTime::currentMSecsSinceEpoch()) + "</div>\n"
- " <div id=\"ajaxPath\" style=\"display: none\">" + server + "/" + channel + "</div>\n"
- " <script src=\"/realtimechat.js\"></script>\n</body>");
- replaceTag(page, "RENDERING_TIMER", QString::number(QDateTime::currentMSecsSinceEpoch() - renderStart));
- QString mainHeader = HEADER_HTML;
- replaceTag(mainHeader, "SIZE", QString::number(QByteArray(page.toUtf8()).size()));
- if (socket->isOpen()) {
- socket->write(mainHeader.toUtf8());
- if (not isHeadRequest) socket->write(page.toUtf8());
- }
- }
- void HttpServer::writeAboutServerPage(QTcpSocket *socket, QString &urlPath, bool isHeadRequest)
- {
- auto renderStart = QDateTime::currentMSecsSinceEpoch();
- urlPath.remove(QRegularExpression("^/serverinformation/"));
- QString server = getWordFromPath(urlPath);
- if (server.isEmpty()) {
- writeErrorPage(socket, "SERVER VALUE IS MISSING");
- return;
- }
- QString originalServerName;
- for (const auto &s: m_servers) {
- if (global::toLowerAndNoSpaces(s.first) == server) {
- originalServerName = s.first;
- }
- }
- if (originalServerName.isEmpty()) {
- writeErrorPage(socket, "SERVER NOT EXIST");
- return;
- }
- QFile main("://html/main.html");
- QString page;
- if (main.open(QIODevice::ReadOnly)) {
- page = main.readAll();
- main.close();
- }
- else {
- if (socket->isOpen()) {
- socket->write(HEADER_404.toUtf8());
- if (not isHeadRequest) socket->write(CRITICAL_ERROR);
- }
- }
- replaceTag(page, "PAGE_TITLE", "About " + originalServerName + " | IRCaBot");
- //// Left menu compilation
- QString htmlServersSectionS;
- for (const auto &s: m_servers) {
- if (s.first.isEmpty()) continue; // empty server name?
- QString htmlServersSection = HTML_SERVER_SECTION;
- if (s.first == originalServerName) {
- htmlServersSection.replace("<div class=\"left_menu__item\">",
- "<div class=\"left_menu__item\" style=\"background: #f0f5fa\">");
- }
- replaceTag(htmlServersSection, "ABOUT_SERVER", "/serverinformation/"+s.first);
- replaceTag(htmlServersSection, "SERVER_NAME", s.first);
- QString htmlChannelLineS;
- for (const auto &c: s.second) {
- QString htmlChannelLine;
- htmlChannelLine = HTML_SERVER_SECTION_CHANNEL;
- replaceTag(htmlChannelLine, "CHANNEL_NAME", c.first);
- QString channelNameForUrl {c.first};
- channelNameForUrl.remove('#');
- QString channelLink = "/" + global::toLowerAndNoSpaces(s.first) + "/" + channelNameForUrl;
- replaceTag(htmlChannelLine, "CHANNEL_LINK", channelLink);
- htmlChannelLineS += htmlChannelLine;
- }
- replaceTag(htmlServersSection, "CHANNELS", htmlChannelLineS);
- bool online {false};
- for (const auto &srv: m_serversOnline) {
- if (srv.first == s.first) {
- online = srv.second;
- break;
- }
- }
- if (online) {
- replaceTag(htmlServersSection, "ONLINE_STATUS", HTML_SERVER_ONLINE_MARKER);
- } else {
- replaceTag(htmlServersSection, "ONLINE_STATUS", HTML_SERVER_OFFLINE_MARKER);
- }
- if (s.first == originalServerName) {
- htmlServersSectionS.push_front(htmlServersSection);
- } else {
- htmlServersSectionS += htmlServersSection;
- }
- }
- replaceTag(page, "SERVERS_SECTION", htmlServersSectionS);
- page.remove(QRegularExpression("<div class=\"main_header\">.*<!-- main_middle -->", QRegularExpression::DotMatchesEverythingOption));
- QString payloadBlock = HTML_PAYLOAD_ABOUT;
- replaceTag(payloadBlock, "ABOUT_TITLE", originalServerName);
- QString aboutBlock;
- QFile about(m_logFolder+server+global::slash+"about_server.txt");
- if (about.open(QIODevice::ReadOnly)) {
- QString rbuffer = about.readLine();
- while (not rbuffer.isEmpty()) {
- if (rbuffer.startsWith('#')) {
- rbuffer = about.readLine();
- continue;
- }
- removeBrakelineSymbols(rbuffer);
- if (not rbuffer.isEmpty()) {
- aboutBlock += rbuffer;
- }
- rbuffer = about.readLine();
- }
- }
- else {
- aboutBlock = "No information provided";
- }
- replaceTag(payloadBlock, "ABOUT_TEXT", aboutBlock);
- replaceTag(page, "PAYLOAD_BLOCK", payloadBlock);
- //// Footer
- replaceTag(page, "VERSION", IRCABOT_VERSION);
- replaceTag(page, "COPYRIGHT_YEAR", COPYRIGHT_YEAR);
- //// Finish
- replaceTag(page, "RENDERING_TIMER", QString::number(QDateTime::currentMSecsSinceEpoch() - renderStart));
- QString mainHeader = HEADER_HTML;
- replaceTag(mainHeader, "SIZE", QString::number(QByteArray(page.toUtf8()).size()));
- if (socket->isOpen()) {
- socket->write(mainHeader.toUtf8());
- if (not isHeadRequest) socket->write(page.toUtf8());
- }
- }
- void HttpServer::writeAjaxAnswer(QTcpSocket *socket, QString &urlPath, bool isHeadRequest)
- {
- if (isHeadRequest) {
- QString header = HEADER_JSON;
- replaceTag(header, "SIZE", "0");
- socket->write(header.toUtf8());
- return;
- }
- //// Validate
- urlPath.remove(QRegularExpression("^/ajax/"));
- QString server = getWordFromPath(urlPath);
- if (server.isEmpty()) {
- writeErrorJson(socket, "Invalid request. Server value is missing!");
- return;
- }
- urlPath.remove(QRegularExpression("^"+server));
- QString channel = getWordFromPath(urlPath);
- if (channel.isEmpty()) {
- writeErrorJson(socket, "Invalid request. Channel value is missing!");
- return;
- }
- QString originalServerName;
- for (const auto &s: m_servers) {
- if (global::toLowerAndNoSpaces(s.first) == server) {
- originalServerName = s.first;
- }
- }
- if (originalServerName.isEmpty()) {
- writeErrorJson(socket, "Invalid request. Server not exist!");
- return;
- }
- QString originalChannelName;
- for (const auto &server: m_servers) {
- for (const auto &channel_users: server.second) {
- if (global::toLowerAndNoSpaces(channel_users.first) == "#"+channel) {
- originalChannelName = global::toLowerAndNoSpaces(channel_users.first);
- }
- }
- }
- if (originalChannelName.isEmpty()) {
- writeErrorJson(socket, "Invalid request. Channel not exist!");
- return;
- }
- //// Parse
- bool userOnlineCounterIsOk {false};
- auto userOnlineCounter = global::getValue(urlPath, "onlineCounter", global::eForWeb).toInt(&userOnlineCounterIsOk);
- if (not userOnlineCounterIsOk) {
- writeErrorJson(socket, "Invalid request: 'onlineCounter' (int) value is missing!");
- return;
- }
- bool userMessageLastIdIsOk {false};
- qint64 userMessageLastId = global::getValue(urlPath, "messageId", global::eForWeb).toLongLong(&userMessageLastIdIsOk);
- if (not userMessageLastIdIsOk) {
- writeErrorJson(socket, "Invalid request: 'messageId' value is missing!");
- return;
- }
- bool userServerStatus = global::getValue(urlPath, "serverStatus", global::eForWeb) == "true";
- //// Building
- QJsonObject jResponse;
- jResponse.insert("status", QJsonValue(true));
- // online server
- if (userServerStatus != m_serversOnline[originalServerName]) {
- jResponse.insert("serverStatusChanged", QJsonValue(true));
- jResponse.insert("serverStatus", QJsonValue(m_serversOnline[originalServerName]));
- } else {
- jResponse.insert("serverStatusChanged", QJsonValue(false));
- }
- // online users
- if (m_servers[originalServerName][originalChannelName].size()-1/*self*/ != userOnlineCounter) {
- jResponse.insert("onlineUsersChanged", QJsonValue(true));
- QJsonObject jOnline;
- int currentOnline = m_servers[originalServerName][originalChannelName].size();
- if (currentOnline > 0) currentOnline -= 1;
- jOnline.insert("count", QJsonValue(currentOnline));
- QJsonArray jOnlineList;
- for (const auto& user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+m_botNick[originalServerName]+"$").match(user).hasMatch()) {
- continue;
- }
- jOnlineList.push_back(QJsonValue(user));
- }
- jOnline.insert("list", jOnlineList);
- jResponse.insert("online", jOnline);
- } else {
- jResponse.insert("onlineUsersChanged", QJsonValue(false));
- }
- // new messages
- bool newMessagesIsExisted {false};
- QString channelId {server+channel};
- QJsonArray jNewMessages;
- qint64 newLastMessageId {userMessageLastId};
- if (not m_messageCache.contains(channelId)) {
- m_messageCache.insert(channelId, new MessagePool);
- consoleLog("Message caching enabled for "+server+"/#"+channel+". Real time reading started.");
- }
- else {
- auto messages = *(m_messageCache[channelId]->getMessages());
- for (const auto& msg: messages) {
- if (userMessageLastId < msg.first) {
- if (not newMessagesIsExisted) newMessagesIsExisted = true;
- QJsonObject jOneNewMessage;
- jOneNewMessage.insert("user", msg.second->getSender());
- jOneNewMessage.insert("text", msg.second->getText());
- jNewMessages.push_back(jOneNewMessage);
- newLastMessageId = msg.first;
- }
- }
- }
- if (newMessagesIsExisted) {
- jResponse.insert("LMIdChanged" /* last message id == LMId */, QJsonValue(true));
- jResponse.insert("newMessages", jNewMessages);
- jResponse.insert("LMId", QJsonValue(QString::number(newLastMessageId)));
- } else {
- jResponse.insert("LMIdChanged", QJsonValue(false));
- }
- //// Finish
- QByteArray response {QJsonDocument(jResponse).toJson()};
- QString header = HEADER_JSON;
- replaceTag(header, "SIZE", QString::number(response.size()));
- socket->write(header.toUtf8());
- socket->write(response);
- }
- /*\
- |*| //////////'''''''''''''\\\\\\\\\\
- |*| HAPPY NEW YEAR IN RUSSIAN PRISON!
- |*| |||||||||| 2022 ||||||||||
- |*| \\\\\\\\\\_____________//////////
- \*/
- Message::Message(const QString& s, const QString& t, qint64 timestamp, QObject *parent) :
- QObject(parent),
- m_sender(s),
- m_text(t),
- m_timestamp(timestamp)
- {
- connect (&m_selfKiller, &QTimer::timeout, [&](){emit outDated(m_timestamp);});
- m_selfKiller.setSingleShot(true);
- m_selfKiller.start(MSECS_TO_AUTOREMOVE_MESSAGES_FROM_BUFFER);
- }
- const QString Message::getSender()
- {
- return m_sender;
- }
- const QString Message::getText()
- {
- return m_text;
- }
- MessagePool::MessagePool(QObject *parent) :
- QObject(parent),
- m_lastPing(QDateTime::currentMSecsSinceEpoch())
- {}
- void MessagePool::saveNewMessage(const QString &nick, const QString &text)
- {
- qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
- Message* newMessage = new Message (nick, text, timestamp);
- connect (newMessage, SIGNAL(outDated(qint64)), this, SLOT (messageToDelete(qint64)));
- m_messages.insert({timestamp, newMessage});
- }
- const std::multimap<qint64, Message *>* MessagePool::getMessages()
- {
- m_lastPing = QDateTime::currentMSecsSinceEpoch();
- return &m_messages;
- }
- qint64 MessagePool::getLastPing()
- {
- return m_lastPing;
- }
- void MessagePool::messageToDelete(qint64 timestamp)
- {
- while (m_messages.find(timestamp) != m_messages.end()) {
- auto it = m_messages.find(timestamp);
- it->second->deleteLater();
- m_messages.erase(it);
- }
- }
|