#include "httpserver.h" #include "ircclient.h" #include "connectiondata.h" #include "global.h" #include "version.h" #include #include #include #include #include #include #include #include constexpr int MAX_MESSAGE_LENGTH_WITHOUT_WBR = 30; constexpr int MAX_NICKNAME_LENGTH_WITHOUT_WBR = 20; constexpr int BUFFER_SIZE = 2048; HttpServer::HttpServer(const QString &address, quint16 port, const QString& logFolder, const QString& mainChannel, QObject *parent) : QObject(parent), m_TcpServer(new QTcpServer), m_mainChannel(mainChannel), m_logFolder(logFolder) { if (not m_TcpServer->listen(QHostAddress(address), port)) { throw std::runtime_error("HttpServer not binded at " + address.toStdString() + " : " + QString::number(port).toStdString()); } else { consoleLog(address + " : " + QString::number(port)); } 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 = " " + displayedName + " "; return result; } std::pair HttpServer::splitUserNameAndMessage(const QString &rawLine) { std::pair result; QString nick {rawLine}; nick.remove(QRegularExpression("\\]\\s.*$")); nick.remove(QRegularExpression("^\\[")); if (nick.isEmpty()) { return result; } QString text {rawLine}; text.remove(QRegularExpression("^\\[[^\\s]*\\]\\s")); if (text.isEmpty()) { return result; } // 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, ""); lastWbr = i; } } } text = text.toHtmlEscaped(); // http links while (QRegularExpression("(^|\\s)http.?://").match(text).hasMatch()) { 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 ! 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, ""); space = i; } } result.first = nick; result.second = text; return result; } void HttpServer::consoleLog(const QString &message) { qInfo().noquote() << "[WEBINTERFACE]" << message; } void HttpServer::debugLog(const QString &req) { QFile log(m_logFolder + "httprequests.log"); if (log.open(QIODevice::WriteOnly | QIODevice::Append)) { log.write(QDateTime::currentDateTime().toString().toUtf8() + ":\n" + req.toUtf8() + "\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(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 == "/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) socket->write("

NOT FOUND

"); } } } } // dynamic page else { writeMainPage(socket, urlPath, isHeadRequest); } socket->disconnectFromHost(); } void HttpServer::ircBotFirstInfo(QString server, QStringList channels) { for (const auto &c: channels) { m_onlineUsers[server][c] = QStringList(); } } void HttpServer::ircUsersOnline(QString server, QString channel, QStringList users) { if (server.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_onlineUsers[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_botsNick[server] = nickname; } 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) { if (socket->isOpen()) { socket->write(HEADER_404.toUtf8()); socket->write("404

NOT FOUND

"); } } void HttpServer::removeBrakelineSymbols(QString &line) { line.remove('\r'); line.remove('\n'); line.remove('\t'); } 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

NOT FOUND

Maybe it's compile time error

"); } } if (isRegexp) { page.replace("", ""); } 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); } 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); } return; } QString originalServerName; for (const auto &s: m_onlineUsers) { if (global::toLowerAndNoSpaces(s.first) == server) { originalServerName = s.first; } } QString originalChannelName; for (const auto &server: m_onlineUsers) { 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); } } 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_onlineUsers) { if (s.first.isEmpty()) continue; // empty server name? QString htmlServersSection = HTML_SERVER_SECTION; 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); } htmlServersSectionS += htmlServersSection; } replaceTag(page, "SERVERS_SECTION", htmlServersSectionS); //// Main section header compilation if (m_onlineUsers.size() > 1) { replaceTag(page, "PAGE_TITLE", originalChannelName + " ("+originalServerName+") | IRCaBot"); } else { replaceTag(page, "PAGE_TITLE", originalChannelName + " | IRCaBot"); } replaceTag(page, "MAIN_HEADER", originalChannelName); if (not m_channelsTopic[originalServerName][originalChannelName].isEmpty()) { m_channelsTopic[originalServerName][originalChannelName].replace('\"', """); page.replace("
", "
"); } QString middlePath = "" + "/" + ""; if (not year.isEmpty()) { middlePath += "" + year + ""; if (not month.isEmpty()) { middlePath += "/" + month + ""; if (not day.isEmpty()) { middlePath += "/" + day; } } } int currentOnline = 0; QString onlineUserS; for (const auto &user: m_onlineUsers[originalServerName][originalChannelName]) { if (QRegularExpression("^(@|\\&|\\+|~)?"+m_botsNick[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("", ""); } 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_onlineUsers) { 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_onlineUsers[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 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 rawMessage = splitUserNameAndMessage(buffer); if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) { buffer = file.readLine(); continue; } counter++; QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE; for (const auto &user: m_onlineUsers[originalServerName][originalChannelName]) { if (QRegularExpression("^.?"+rawMessage.first+"$").match(user).hasMatch()) { message.replace("
", "
"); break; } } rawMessage.first = rawMessage.first.toHtmlEscaped(); 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 rawMessage = splitUserNameAndMessage(buffer); if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) { buffer = file.readLine(); continue; } counter++; QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE; for (const auto &user: m_onlineUsers[originalServerName][originalChannelName]) { if (QRegularExpression("^.?"+rawMessage.first+"$").match(user).hasMatch()) { message.replace("
", "
"); break; } } rawMessage.first = rawMessage.first.toHtmlEscaped(); 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 rawMessage = splitUserNameAndMessage(buffer); if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) { buffer = file.readLine(); continue; } counter++; QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE; for (const auto &user: m_onlineUsers[originalServerName][originalChannelName]) { if (QRegularExpression("^.?"+rawMessage.first+"$").match(user).hasMatch()) { message.replace("
", "
"); break; } } rawMessage.first = rawMessage.first.toHtmlEscaped(); 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", "" + f + " (" + 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", "" + a + " (" + 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 rawMessage = splitUserNameAndMessage(buffer); if (rawMessage.first.isEmpty() or rawMessage.second.isEmpty()) { buffer = file.readLine(); continue; } QString message = HTML_PAYLOAD_LIST_CHAT_MESSAGE; for (const auto &user: m_onlineUsers[originalServerName][originalChannelName]) { if (QRegularExpression("^(@|\\&|\\+|~)?"+rawMessage.first+"$").match(user).hasMatch()) { message.replace("
", "
"); break; } } rawMessage.first = rawMessage.first.toHtmlEscaped(); 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()); } }