/* Copyright 2018 Denes Matetelki This file is part of webfish. webfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License v3 as published by the Free Software Foundation. webfish is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License v3 for more details. You should have received a copy of the GNU General Public License v3 along with webfish. If not, see https://www.gnu.org/licenses/gpl-3.0.html. */ #ifndef WEBHOOK_MESSAGE_HPP #define WEBHOOK_MESSAGE_HPP #include #include #include // put_time #include #include #include #include class WebhookMessage : public Message { public: struct WebhookMessageParam { const std::string script_path; const std::string client; const std::string secret; }; WebhookMessage( void *msgParam = 0) : Message(msgParam) { TRACE; } bool buildMessage( const void *msgPart, const size_t msgLen ) { TRACE; m_buffer = std::string( (const char*) msgPart, msgLen ); /** @todo use it! * We are very rude now: * - bad request? Reply & close connection. * - correct request? Reply after the first chunk, while the client is still talking */ onMessageReady(); return true; } void onMessageReady() { TRACE; LOG_BEGIN(Logger::INFO) LOG_PROP("buffer", m_buffer) LOG_PROP("host", m_connection->getHost()) LOG_PROP("port", m_connection->getPort()) LOG_END("Got message."); std::string reply; WebhookMessageParam* p = reinterpret_cast(m_param); try { if (m_connection->getHost() != p->client) { reply = generateReply("403 Forbidden"); throw std::runtime_error("Client is not from the hostname we expect."); } if (m_buffer.find("POST /gitea-webhook/post HTTP/1.1") != 0) { reply = generateReply("400 Bad Request"); throw std::runtime_error("Received msg is not a gitea-webhook"); } const std::string s = "\"secret\": \"" + p->secret + "\""; if (m_buffer.find(s) == std::string::npos) { reply = generateReply("401 Unauthorized"); throw std::runtime_error("Received msg does not contain the secret."); } } catch (const std::exception& e) { LOG (Logger::WARNING, e.what()); m_connection->send(reply.c_str(), reply.length()); m_buffer.clear(); dynamic_cast(m_connection)->disconnect(); return; } int status = system(p->script_path.c_str()); reply = generateReply(status == 0 ? "200 OK": "500 Internal Server Error"); m_connection->send(reply.c_str(), reply.length()); m_buffer.clear(); } std::string generateReply(const std::string status) const { const auto t = std::time(nullptr); const auto tm = *std::localtime(&t); std::ostringstream oss; oss << "HTTP/1.1 " << status << "\r\n"; // Fri, 08 Feb 2019 12:35:37 GMT oss << "Date: " << std::put_time(&tm, "%a, %d %b %Y %T %Z") << "\r\n"; oss << "Server: webfish(0.1)\r\n" \ "Content-Type: text/plain;charset=utf-8\r\n" \ "Content-Length: 10\r\n" \ "Connection: close\r\n" \ "\r\n" \ "Processed\n"; return oss.str(); } Message* clone() { TRACE; return new WebhookMessage(m_param); } std::string getBuffer() { TRACE; return m_buffer; } protected: size_t getExpectedLength() { TRACE; return 0; } }; #endif // WEBHOOK_MESSAGE_HPP