#ifndef HTTPSERVER_H #define HTTPSERVER_H #include #include #include #include #include #include constexpr int MSECS_TO_AUTOREMOVE_MESSAGES_FROM_BUFFER {15000}; // 15 secs constexpr int MAX_MESSAGE_LENGTH_WITHOUT_WBR = 30; constexpr int MAX_NICKNAME_LENGTH_WITHOUT_WBR = 20; constexpr int BUFFER_SIZE = 2048; const QString HTTP_ACTUAL_ETAG {__DATE__ __TIME__}; const QString ANCHOR_SUFFIX {"msg"}; const QString RED_1 {"#753d08"}; const QString RED_2 {"#b83e3e"}; const QString GREEN_1 {"#60940c"}; const QString GREEN_2 {"#27940c"}; class NickColorist { public: const QString& getGreenColor(bool next = true); const QString& getRedColor(bool next = true); private: const QString* m_currentGreen = &GREEN_1; const QString* m_currentRed = &RED_1; }; class Message : public QObject { Q_OBJECT public: Message(const QString& s, const QString& t, qint64 timestamp, QObject *parent = nullptr); const QString getSender(); const QString getText(); private: QString m_sender; QString m_text; qint64 m_timestamp; QTimer m_selfKiller; signals: void outDated(qint64); }; class MessagePool : public QObject { Q_OBJECT public: MessagePool(QObject *parent = nullptr); void saveNewMessage(const QString& nick, const QString& text); const std::multimap* getMessages(); qint64 getLastPing(); private: qint64 m_lastPing; std::multimap m_messages; // timestamp, message public slots: void messageToDelete(qint64 timestamp); }; ////////////////////////////////////////////////// class HttpServer : public QObject { Q_OBJECT public: explicit HttpServer(const QString& address, quint16 port, const QString& logFolder, const QString& serviceName, const QString& serviceEmoji, bool ajaxIsDisabled, QObject *parent = nullptr); ~HttpServer(); private: QString convertToClickableLink(const QString &httpLine); std::pair splitUserNameAndMessage(const QString& rawLine); QString getRequestPath(const QString &req); QString getWordFromPath(const QString& path); void writeMainPage(QTcpSocket* socket, bool isHeadRequest); void writeCustomPicture(QTcpSocket* socket, QString& urlPath, bool isHeadRequest); void writeRegularPage(QTcpSocket* socket, QString& urlPath, bool isHeadRequest); void writeRealTimeChatPage(QTcpSocket* socket, QString& urlPath, bool isHeadRequest); void writeAboutServerPage(QTcpSocket* socket, QString& urlPath, bool isHeadRequest); void writeAjaxAnswer(QTcpSocket* socket, QString& urlPath, bool isHeadRequest); void writeErrorPage(QTcpSocket*, const QString& text = "NOT FOUND"); void writeErrorJson(QTcpSocket*, const QString& text = ""); void removeBrakelineSymbols(QString& line); inline void replaceTag(QString& page, const QString& tag, const QString& payload); void consoleLog(const QString &message); void debugLog(const QString &url); QTcpServer* m_TcpServer; QString m_serviceButton; QString m_serviceName; QString m_dataFolder; bool m_ajaxIsDisabled; std::map> m_servers; // server, {channel, users} std::map> m_channelsTopic; // server, {channel, topic} std::map m_serversOnline; std::map m_botNick; QMap m_messageCache; // server+channel const QString HEADER_HTML = "\ HTTP/1.1 200 OK\r\n\ Content-Type: text/html; charset=utf-8\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_TEXT = "\ HTTP/1.1 200 OK\r\n\ Content-Type: text/plain; charset=utf-8\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_CSS = "\ HTTP/1.1 200 OK\r\n\ Content-Type: text/css\r\n\ Cache-Control: public, max-age=31536000\r\n\ ETag: \""+HTTP_ACTUAL_ETAG+"\"\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_SVG = "\ HTTP/1.1 200 OK\r\n\ Content-Type: image/svg+xml\r\n\ Cache-Control: public, max-age=31536000\r\n\ ETag: \""+HTTP_ACTUAL_ETAG+"\"\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_ICO = "\ HTTP/1.1 200 OK\r\n\ Content-Type: image/ico\r\n\ Cache-Control: public, max-age=31536000\r\n\ ETag: \""+HTTP_ACTUAL_ETAG+"\"\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_IMG = "\ HTTP/1.1 200 OK\r\n\ Content-Type: image/{{TYPE}}\r\n\ Cache-Control: public, max-age=31536000\r\n\ ETag: \""+HTTP_ACTUAL_ETAG+"\"\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_JS = "\ HTTP/1.1 200 OK\r\n\ Content-Type: text/javascript; charset=utf-8\r\n\ Cache-Control: public, max-age=31536000\r\n\ ETag: \""+HTTP_ACTUAL_ETAG+"\"\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_JSON = "\ HTTP/1.1 200 OK\r\n\ Content-Type: application/json; charset=utf-8\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_MP3 = "\ HTTP/1.1 200 OK\r\n\ Content-Type: audio/mpeg;\r\n\ ETag: \""+HTTP_ACTUAL_ETAG+"\"\r\n\ Content-Length: {{SIZE}}\r\n\r\n"; const QString HEADER_304 = "HTTP/1.1 304 Not Modified\r\nContent-Length: 0\r\n\r\n"; const QString HEADER_404 = "HTTP/1.1 404 Not found\r\n\r\n"; const QString HTML_SERVER_SECTION = "\
\n\
\n\ {{ONLINE_STATUS}}\n\ {{SERVER_NAME}}\n\
\n\
\n\ {{CHANNELS}}\
\n\
\n"; const QString HTML_SERVER_ONLINE_MARKER = "✅"; const QString HTML_SERVER_OFFLINE_MARKER = "❌"; const QString HTML_SERVER_SECTION_CHANNEL = "\ {{CHANNEL_NAME}}\n"; const QString HTML_SERVER_SECTION_CHANNEL_SELECTED = "\ {{CHANNEL_NAME}}\n"; const QString HTML_ONLINE_POINT = "\
\n\ {{NICKNAME}}\n\
\n"; const QString HTML_PAYLOAD_LIST_POINT_FOLDER = "\ \n\
\n\
\n\
\n\ {{POINT_CONTENT}}\n\
\n\
\n"; const QString HTML_PAYLOAD_LIST_POINT_MESSAGE = "\ \n\
\n\
\n\
\n\ {{POINT_CONTENT}}\n\
\n\
\n"; const QString HTML_PAYLOAD_LIST_CHAT_MESSAGE = "\
\n\ \n\ {{USERNAME}}\n\ \n\ \n\ {{MESSAGE_TEXT}}\n\ \n\
\n"; const QString HTML_PAYLOAD_ADDITIONAL_ARROWS = "\ \n"; const QString HTML_PAYLOAD_ADDITIONAL_NOTIFY = "\
\n"; const QString HTML_PAYLOAD_MIDDLEPATH_ARROW_PREV = "\ \n"; const QString HTML_PAYLOAD_MIDDLEPATH_ARROW_PREV_FALSE = "\ \n"; const QString HTML_PAYLOAD_MIDDLEPATH_ARROW_NEXT = "\ \n"; const QString HTML_PAYLOAD_MIDDLEPATH_ARROW_NEXT_FALSE = "\ \n"; const QString HTML_PAYLOAD_ERROR = "\
\n\ {{ERROR_TITLE}}
\n\ {{ERROR_TEXT}}\n\
"; const QString HTML_PAYLOAD_ABOUT = "\
\n\ {{ABOUT_TITLE}}
\n\ {{ABOUT_TEXT}}\n\
"; const QString HTML_LEFT_MENU_MAIN_POINT = "\
\n\
\n\ {{EMOJI}}\n\ {{SERVICE_NAME}}\n\
\n\
\n"; private slots: void acceptor(); void reader(); public slots: void ircBotFirstInfo(QString server, QStringList channels); // needed for permanent offline servers void ircUsersOnline(QString server, QString channel, QStringList users); void ircChannelTopic(QString server, QString channel, QString topic); void ircServerOnline(QString server, quint8 status /*0 or 1*/); void ircBotNick(QString server, QString nickname); void ircMessageCache(QString server, QString channel, QString nick, QString text); }; #endif // HTTPSERVER_H