123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274 |
- #include "httpserver.h"
- #include "ircclient.h"
- #include "connectiondata.h"
- #include "global.h"
- #include <QRegularExpression>
- #include <QDirIterator>
- #include <QDateTime>
- #include <QHostAddress>
- #include <QTcpSocket>
- #include <QDebug>
- #include <QJsonDocument>
- #include <QJsonArray>
- #include <QJsonObject>
- #include <QMutex>
- #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& dataFolder,
- const QString& serviceName, const QString& serviceEmoji, bool ajaxIsDisabled, QObject *parent) :
- QObject(parent),
- m_TcpServer(new QTcpServer),
- m_serviceName(serviceName),
- m_dataFolder(dataFolder),
- 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("http://" +address + ":" + QString::number(port) + "/");
- if (m_ajaxIsDisabled) {
- consoleLog("JavaScript on webpages removed and AJAX disabled!");
- }
- if (not QFile::exists(m_dataFolder+"main_page.txt")) {
- QFile mp(m_dataFolder+"main_page.txt");
- if (mp.open(QIODevice::WriteOnly)) {
- mp.write("# Main page file.\n"
- "# HTML is supported. For line breaks, use <br> or <p></p>.\n\n"
- "<center>\n"
- "# Your images from \"" + m_dataFolder.toUtf8() + "custom_images\" must have URL \"/~images/\"\n"
- "<img src=\"/~images/example.png\">\n"
- "<p><span style=\"color: gray\">local time:</span> " +LOCAL_TIME_MARKER_FOR_MAIN_PAGE.toUtf8()+ "<br>\n"
- "<span style=\"color: gray\">daily requests:</span> " +DAILY_REQUESTS_COUNTER_VALUE_FOR_MAIN_PAGE.toUtf8()+ "</p>\n"
- "</center>\n");
- mp.close();
- }
- else {
- throw std::runtime_error("main_page.txt not exist and creating failed");
- }
- }
- QDir dir {m_dataFolder};
- if (not dir.cd("custom_images")) {
- if (dir.mkdir("custom_images")) {
- dir.cd("custom_images");
- QFile examplePng(dir.path()+global::slash+"example.png");
- if (examplePng.open(QIODevice::WriteOnly)) {
- QFile nativePng("://html/custom_img_example.png");
- if (nativePng.open(QIODevice::ReadOnly)) {
- examplePng.write(nativePng.readAll());
- nativePng.close();
- examplePng.close();
- }
- }
- } else {
- consoleLog("Creating folder \"custom_images\" failed");
- }
- }
- m_serviceButton = HTML_LEFT_MENU_MAIN_POINT;
- replaceTag(m_serviceButton, "EMOJI", serviceEmoji);
- replaceTag(m_serviceButton, "SERVICE_NAME", serviceName);
- 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_dataFolder + "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();
- static uint sockcount = 0;
- if (++sockcount < 20)
- {
- connect(socket, &QTcpSocket::readyRead, this, &HttpServer::reader);
- connect(socket, &QTcpSocket::disconnected, socket, &QTcpSocket::deleteLater);
- connect(socket, &QTcpSocket::disconnected, [&]() {--sockcount;} );
- }
- else
- {
- socket->close();
- socket->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);
- 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 == "/") {
- ++m_requestCounterPlainInfo;
- writeMainPage(socket, isHeadRequest);
- }
- 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 {
- ++m_requestCounterAjax;
- writeAjaxAnswer(socket, urlPath, isHeadRequest);
- }
- }
- else if (urlPath.startsWith("/~realtime/")) {
- if (m_ajaxIsDisabled) {
- writeErrorPage(socket, "DISABLED"
- "<p>No JS, no AJAX, no real time reading!</p>");
- } else {
- ++m_requestCounterPlainInfo;
- writeRealTimeChatPage(socket, urlPath, isHeadRequest);
- }
- }
- else if (urlPath.startsWith("/~images/")) {
- writeCustomPicture(socket, urlPath, isHeadRequest);
- }
- else {
- ++m_requestCounterPlainInfo;
- writeRegularPage(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::writeMainPage(QTcpSocket *socket, bool isHeadRequest)
- {
- auto renderStart = QDateTime::currentMSecsSinceEpoch();
- 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", m_serviceName + " | IRC logger");
- //// 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", "/"+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);
- }
- htmlServersSectionS += htmlServersSection;
- }
- QString serviceButtonSelected {m_serviceButton};
- serviceButtonSelected.replace("class=\"left_menu__mainitem\"",
- "class=\"left_menu__mainitem\" style=\"opacity: 1\"");
- htmlServersSectionS.push_front(serviceButtonSelected);
- replaceTag(page, "SERVERS_SECTION", htmlServersSectionS);
- page.remove(QRegularExpression("<div class=\"main_header\">.*<!-- main_middle -->", QRegularExpression::DotMatchesEverythingOption));
- QString payloadBlock = HTML_PAYLOAD_ABOUT;
- payloadBlock.remove("<span style=\"color: green; display: block; font-size: 24px; text-align: center;\">{{ABOUT_TITLE}}</span><br>\n");
- QString aboutBlock;
- QFile mp(m_dataFolder+global::slash+"main_page.txt");
- if (mp.open(QIODevice::ReadOnly)) {
- QString rbuffer = mp.readLine();
- while (not rbuffer.isEmpty()) {
- if (rbuffer.startsWith('#')) {
- rbuffer = mp.readLine();
- continue;
- }
- removeBrakelineSymbols(rbuffer);
- if (not rbuffer.isEmpty()) {
- aboutBlock += rbuffer;
- }
- rbuffer = mp.readLine();
- }
- aboutBlock.replace(LOCAL_TIME_MARKER_FOR_MAIN_PAGE, QTime::currentTime().toString(Qt::DateFormat::ISODate));
- QString reqCounterStr = QString::number(*m_requestCounterPlainInfo.value());
- if (not m_ajaxIsDisabled)
- {
- reqCounterStr += " (html) / " + QString::number(*m_requestCounterAjax.value()) + " (ajax)";
- }
- aboutBlock.replace(DAILY_REQUESTS_COUNTER_VALUE_FOR_MAIN_PAGE, reqCounterStr);
- }
- else {
- aboutBlock = "No information provided";
- }
- replaceTag(payloadBlock, "ABOUT_TEXT", aboutBlock);
- replaceTag(page, "PAYLOAD_BLOCK", payloadBlock);
- //// Footer
- replaceTag(page, "VERSION", global::IRCABOT_VERSION);
- replaceTag(page, "COPYRIGHT_YEAR", global::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::writeCustomPicture(QTcpSocket *socket, QString &urlPath, bool isHeadRequest)
- {
- QString fileName {urlPath};
- fileName.remove(QRegularExpression("^/~images/"));
- QFile f {m_dataFolder+global::slash+"custom_images"+global::slash+fileName};
- if (not f.exists()) {
- writeErrorPage(socket, fileName + "<p>IMAGE NOT FOUND</p>");
- return;
- }
- QByteArray file;
- if (f.open(QIODevice::ReadOnly)) {
- file = f.readAll();
- f.close();
- }
- else {
- writeErrorPage(socket, "FORBIDDEN");
- return;
- }
- QString picHeader = HEADER_IMG;
- QString fileType {fileName};
- fileType.remove(QRegularExpression(".*\\."));
- replaceTag(picHeader, "TYPE", fileType);
- replaceTag(picHeader, "SIZE", QString::number(file.size()));
- if (socket->isOpen()) {
- socket->write(picHeader.toUtf8());
- if (not isHeadRequest) socket->write(file);
- }
- }
- void HttpServer::writeErrorPage(QTcpSocket *socket, const QString& text)
- {
- if (socket->isOpen()) {
- socket->write(HEADER_404.toUtf8());
- socket->write("<title>404</title>"
- "<center>"
- "<p><h1>"+text.toUtf8()+"</H1></p>"
- "<p><h3>[<a href=\"/\" style=\"text-decoration: none\">main page</a>]</h3></p>"
- "</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::writeRegularPage(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);
- }
- QString server = getWordFromPath(urlPath);
- QDir fsPath(m_dataFolder+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()) {
- writeAboutServerPage(socket, server, isHeadRequest);
- return;
- }
- 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(); }
- 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>");
- }
- //// 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", "/"+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;
- }
- }
- htmlServersSectionS.push_front(m_serviceButton);
- replaceTag(page, "SERVERS_SECTION", htmlServersSectionS);
- //// Main section header compilation
- QString& topic = m_channelsTopic[originalServerName][originalChannelName];
- topic = topic.replace('\"', """);
- QString titlePostfix = " | " + m_serviceName;
- 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", "/~realtime/"+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>";
- }
- }
- 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 == "<a style=\"text-decoration: none\" href=\"/"+server+"/"+channel+"\">" + "/" + "</a>") {
- 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;
- std::pair<QString,QString> lastGreenNickname; // nick, color
- std::pair<QString,QString> lastRedNickname; // nick, color
- quint64 messageAnchorCounter = 0;
- NickColorist nickColorist;
- bool colorEdited = false;
- // Search request
- if (not searchRequest.isEmpty()) {
- static QMutex searchMtx;
- uint counter = 0;
- QRegularExpression userRgx(searchRequest, QRegularExpression::CaseInsensitiveOption);
- bool rgxIsValid = false;
- if (isRegexp and userRgx.isValid()) {
- rgxIsValid = true;
- }
- if (searchMtx.tryLock())
- {
- consoleLog("Search request (" + server + "): " + searchRequest);
- QStringList paths;
- QDirIterator it(fsPath.path());
- while (it.hasNext()) {
- QString currentPath = it.next();
- if (currentPath.endsWith(".") or currentPath.endsWith("..")) continue;
- QString logFolder = m_dataFolder;
- #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);
- messageAnchorCounter++;
- 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\"");
- }
- message.remove(" name=\"{{ANCHOR}}\"");
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+rawMessage.first+"$").match(user).hasMatch()) {
- if (lastGreenNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastGreenNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getGreenColor());
- lastGreenNickname.first = rawMessage.first;
- lastGreenNickname.second = nickColorist.getGreenColor(false);
- }
- break;
- }
- }
- if (not colorEdited) {
- if (lastRedNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastRedNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getRedColor());
- lastRedNickname.first = rawMessage.first;
- lastRedNickname.second = nickColorist.getRedColor(false);
- }
- }
- QString logFolder {m_dataFolder};
- logFolder.remove(QRegularExpression(".$"));
- QString link {path};
- link.remove(logFolder);
- link.remove(QRegularExpression("\\.txt$"));
- replaceTag(message, "ANCHOR", link+"#"+ANCHOR_SUFFIX+QString::number(messageAnchorCounter-1));
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- matchedPathsAndMessages[path].push_back(message);
- }
- buffer = file.readLine();
- }
- file.close();
- messageAnchorCounter = 0;
- }
- }
- 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()) {
- messageAnchorCounter++;
- 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\"");
- }
- message.remove(" name=\"{{ANCHOR}}\"");
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+rawMessage.first+"$").match(user).hasMatch()) {
- if (lastGreenNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastGreenNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getGreenColor());
- lastGreenNickname.first = rawMessage.first;
- lastGreenNickname.second = nickColorist.getGreenColor(false);
- }
- break;
- }
- }
- if (not colorEdited) {
- if (lastRedNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastRedNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getRedColor());
- lastRedNickname.first = rawMessage.first;
- lastRedNickname.second = nickColorist.getRedColor(false);
- }
- }
- QString logFolder {m_dataFolder};
- logFolder.remove(QRegularExpression(".$"));
- QString link {path};
- link.remove(logFolder);
- link.remove(QRegularExpression("\\.txt$"));
- replaceTag(message, "ANCHOR", link+"#"+ANCHOR_SUFFIX+QString::number(messageAnchorCounter-1));
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- matchedPathsAndMessages[path].push_back(message);
- }
- buffer = file.readLine();
- }
- file.close();
- messageAnchorCounter = 0;
- }
- }
- }
- 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);
- }
- 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);
- messageAnchorCounter++;
- 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\"");
- }
- message.remove(" name=\"{{ANCHOR}}\"");
- for (const auto &user: m_servers[originalServerName][originalChannelName]) {
- if (QRegularExpression("^(.*;|~|@|\\&|\\+)?"+rawMessage.first+"$").match(user).hasMatch()) {
- if (lastGreenNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastGreenNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getGreenColor());
- lastGreenNickname.first = rawMessage.first;
- lastGreenNickname.second = nickColorist.getGreenColor(false);
- }
- break;
- }
- }
- if (not colorEdited) {
- if (lastRedNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastRedNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getRedColor());
- lastRedNickname.first = rawMessage.first;
- lastRedNickname.second = nickColorist.getRedColor(false);
- }
- }
- QString logFolder {m_dataFolder};
- logFolder.remove(QRegularExpression(".$"));
- QString link {path};
- link.remove(logFolder);
- link.remove(QRegularExpression("\\.txt$"));
- replaceTag(message, "ANCHOR", link+"#"+ANCHOR_SUFFIX+QString::number(messageAnchorCounter-1));
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- matchedPathsAndMessages[path].push_back(message);
- }
- buffer = file.readLine();
- }
- file.close();
- messageAnchorCounter = 0;
- }
- }
- 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_dataFolder};
- 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";
- }
- }
- }
- int lastWbr = 0;
- for (int i = 0; i < searchRequest.size(); i++) {
- if (i-lastWbr > MAX_MESSAGE_LENGTH_WITHOUT_WBR) {
- searchRequest.insert(i, "<wbr>");
- lastWbr = i;
- }
- }
- searchMtx.unlock();
- } // searchMtx.tryLock()
- else
- {
- consoleLog("Search request (" + server + "): " + searchRequest + " rejected by mutex");
- payloadBlock = HTML_PAYLOAD_ERROR;
- replaceTag(payloadBlock, "ERROR_TITLE", "Try later");
- replaceTag(payloadBlock, "ERROR_TEXT", "Please try again later. The search service is busy.");
- }
- middlePath += " <span style=\"color: #4f9c65\">" + searchRequest.toHtmlEscaped() + "</span> ";
- 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 {
- middlePath += "/" + day;
- 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()) {
- if (lastGreenNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastGreenNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getGreenColor());
- lastGreenNickname.first = rawMessage.first;
- lastGreenNickname.second = nickColorist.getGreenColor(false);
- }
- break;
- }
- }
- if (not colorEdited) {
- if (lastRedNickname.first == rawMessage.first) {
- replaceTag(message, "COLOR", lastRedNickname.second);
- }else{
- replaceTag(message, "COLOR", nickColorist.getRedColor());
- lastRedNickname.first = rawMessage.first;
- lastRedNickname.second = nickColorist.getRedColor(false);
- }
- }
- message.replace("href=\"", "href=\"#");
- replaceTag(message, "ANCHOR", ANCHOR_SUFFIX+QString::number(messageAnchorCounter++));
- replaceTag(message, "USERNAME", rawMessage.first);
- replaceTag(message, "MESSAGE_TEXT", rawMessage.second);
- payloadBlock += message;
- buffer = file.readLine();
- }
- file.close();
- // arrows to prev & next days
- bool prevDayFound {false};
- uint prevDayUInt {day.toUShort()};
- uint prevMonthUInt {month.toUShort()};
- uint prevYearUInt {year.toUShort()};
- QDir fsPathForPrev {fsPath};
- for(uint i = prevDayUInt-1; i >= 1; i--) { // current month
- QString d {fsPathForPrev.path()+global::slash};
- if (i < 10) d += "0";
- d += QString::number(i)+".txt";
- if (QFile::exists(d)) {
- prevDayUInt = i;
- prevDayFound = true;
- break;
- }
- }
- if (not prevDayFound) { // other month
- fsPathForPrev.cdUp();
- bool mf {false};
- for (uint i = prevMonthUInt-1; i >= 1; i--) {
- QString m;
- if (i < 10) m += "0";
- m += QString::number(i);
- if (fsPathForPrev.cd(m)) {
- prevMonthUInt = i;
- mf = true;
- break;
- }
- }
- if (mf) {
- for(uint i = 31; i >= 1; i--) {
- QString d {fsPathForPrev.path()+global::slash};
- if (i < 10) d += "0";
- d += QString::number(i)+".txt";
- if (QFile::exists(d)) {
- prevDayUInt = i;
- prevDayFound = true;
- break;
- }
- }
- }
- }
- if (not prevDayFound) { // other year
- fsPathForPrev.cdUp();
- bool yf {false};
- for (uint i = prevYearUInt-1; i > prevYearUInt-21; i--) {
- if (fsPathForPrev.cd(QString::number(i))) {
- prevYearUInt = i;
- yf = true;
- break;
- }
- }
- if (yf) {
- bool mf {false};
- for (uint i = 12; i >= 1; i--) {
- QString m;
- if (i < 10) m += "0";
- m += QString::number(i);
- if (fsPathForPrev.cd(m)) {
- prevMonthUInt = i;
- mf = true;
- break;
- }
- }
- if (mf) {
- for(uint i = 31; i >= 1; i--) {
- QString d {fsPathForPrev.path()+global::slash};
- if (i < 10) d += "0";
- d += QString::number(i)+".txt";
- if (QFile::exists(d)) {
- prevDayUInt = i;
- prevDayFound = true;
- break;
- }
- }
- }
- }
- }
- QString leftArrow;
- if (prevDayFound) {
- leftArrow = HTML_PAYLOAD_MIDDLEPATH_ARROW_PREV;
- QString link = "/"+server+"/"+channel+"/"+QString::number(prevYearUInt)+"/";
- QString m;
- if (prevMonthUInt < 10) m += "0";
- m += QString::number(prevMonthUInt);
- link += m+"/";
- QString d;
- if (prevDayUInt < 10) d += "0";
- d += QString::number(prevDayUInt);
- link += d;
- replaceTag(leftArrow, "LINK", link);
- } else {
- leftArrow = HTML_PAYLOAD_MIDDLEPATH_ARROW_PREV_FALSE;
- }
- middlePath += leftArrow;
- bool nextDayFound {false};
- uint nextDayUInt {day.toUShort()};
- uint nextMonthUInt {month.toUShort()};
- uint nextYearUInt {year.toUShort()};
- QDir fsPathForNext {fsPath};
- for(uint i = nextDayUInt+1; i <= 31; i++) { // current month
- QString d {fsPathForNext.path()+global::slash};
- if (i < 10) d += "0";
- d += QString::number(i)+".txt";
- if (QFile::exists(d)) {
- nextDayUInt = i;
- nextDayFound = true;
- break;
- }
- }
- if (not nextDayFound) { // other month
- fsPathForNext.cdUp();
- bool mf {false};
- for (uint i = nextMonthUInt+1; i <= 12; i++) {
- QString m;
- if (i < 10) m += "0";
- m += QString::number(i);
- if (fsPathForNext.cd(m)) {
- nextMonthUInt = i;
- mf = true;
- break;
- }
- }
- if (mf) {
- for(uint i = 1; i <= 31; i++) {
- QString d {fsPathForNext.path()+global::slash};
- if (i < 10) d += "0";
- d += QString::number(i)+".txt";
- if (QFile::exists(d)) {
- nextDayUInt = i;
- nextDayFound = true;
- break;
- }
- }
- }
- }
- if (not nextDayFound) { // other year
- fsPathForNext.cdUp();
- bool yf {false};
- for (uint i = nextYearUInt+1; i < nextYearUInt+20; i++) {
- if (fsPathForNext.cd(QString::number(i))) {
- nextYearUInt = i;
- yf = true;
- break;
- }
- }
- if (yf) {
- bool mf {false};
- for (uint i = 1; i <= 12; i++) {
- QString m;
- if (i < 10) m += "0";
- m += QString::number(i);
- if (fsPathForNext.cd(m)) {
- nextMonthUInt = i;
- mf = true;
- break;
- }
- }
- if (mf) {
- for(uint i = 1; i <= 31; i++) {
- QString d {fsPathForNext.path()+global::slash};
- if (i < 10) d += "0";
- d += QString::number(i)+".txt";
- if (QFile::exists(d)) {
- nextDayUInt = i;
- nextDayFound = true;
- break;
- }
- }
- }
- }
- }
- QString rightArrow;
- if (nextDayFound) {
- rightArrow = HTML_PAYLOAD_MIDDLEPATH_ARROW_NEXT;
- QString link = "/"+server+"/"+channel+"/"+QString::number(nextYearUInt)+"/";
- QString m;
- if (nextMonthUInt < 10) m += "0";
- m += QString::number(nextMonthUInt);
- link += m+"/";
- QString d;
- if (nextDayUInt < 10) d += "0";
- d += QString::number(nextDayUInt);
- link += d;
- replaceTag(rightArrow, "LINK", link);
- } else {
- rightArrow = HTML_PAYLOAD_MIDDLEPATH_ARROW_NEXT_FALSE;
- }
- middlePath += rightArrow;
- }
- }
- 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", global::IRCABOT_VERSION);
- replaceTag(page, "COPYRIGHT_YEAR", global::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("^/~realtime/"));
- 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", "/"+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 = "/~realtime/" + 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;
- }
- }
- htmlServersSectionS.push_front(m_serviceButton);
- replaceTag(page, "SERVERS_SECTION", htmlServersSectionS);
- //// Main section header compilation
- QString& topic = m_channelsTopic[originalServerName][originalChannelName];
- topic = topic.replace('\"', """);
- QString titlePostfix = " | " + m_serviceName;
- 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_dataFolder+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;
- message.remove("name=\"{{ANCHOR}}\" href=\"{{ANCHOR}}\" ");
- 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", global::IRCABOT_VERSION);
- replaceTag(page, "COPYRIGHT_YEAR", global::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 &server, bool isHeadRequest)
- {
- auto renderStart = QDateTime::currentMSecsSinceEpoch();
- 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 + " | " + m_serviceName);
- //// 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", "/"+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;
- }
- }
- htmlServersSectionS.push_front(m_serviceButton);
- 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_dataFolder+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", global::IRCABOT_VERSION);
- replaceTag(page, "COPYRIGHT_YEAR", global::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);
- }
- }
- const QString &NickColorist::getGreenColor(bool next)
- {
- if (not next) {
- return *m_currentGreen;
- }
- if (m_currentGreen == &GREEN_1) {
- m_currentGreen = &GREEN_2;
- }else{
- m_currentGreen = &GREEN_1;
- }
- return *m_currentGreen;
- }
- const QString &NickColorist::getRedColor(bool next)
- {
- if (not next) {
- return *m_currentRed;
- }
- if (m_currentRed == &RED_1) {
- m_currentRed = &RED_2;
- }else{
- m_currentRed = &RED_1;
- }
- return *m_currentRed;
- }
- void RequestCounter::operator++()
- {
- if (m_lastUpdateDate != QDate::currentDate()) {
- m_requestsCounter = 1;
- m_lastUpdateDate = QDate::currentDate();
- }else{
- m_requestsCounter++;
- }
- }
- quint64 *RequestCounter::value()
- {
- return &m_requestsCounter;
- }
|