/* * * (C) 2012-2024 Anope Team * Contact us at team@anope.org * * Please read COPYING and README for further details. */ #pragma once enum HTTPError { HTTP_ERROR_OK = 200, HTTP_FOUND = 302, HTTP_BAD_REQUEST = 400, HTTP_PAGE_NOT_FOUND = 404, HTTP_NOT_SUPPORTED = 505 }; /* A message to someone */ struct HTTPReply final { HTTPError error = HTTP_ERROR_OK; Anope::string content_type; std::map headers; typedef std::list > cookie; std::vector cookies; HTTPReply() = default; HTTPReply &operator=(const HTTPReply &) = default; HTTPReply(const HTTPReply &other) : error(other.error), length(other.length) { content_type = other.content_type; headers = other.headers; cookies = other.cookies; for (const auto &datum : other.out) out.push_back(new Data(datum->buf, datum->len)); } ~HTTPReply() { for (const auto *datum : out) delete datum; out.clear(); } struct Data final { char *buf; size_t len; Data(const char *b, size_t l) { this->buf = new char[l]; memcpy(this->buf, b, l); this->len = l; } ~Data() { delete [] buf; } }; std::deque out; size_t length = 0; void Write(const Anope::string &message) { this->out.push_back(new Data(message.c_str(), message.length())); this->length += message.length(); } void Write(const char *b, size_t l) { this->out.push_back(new Data(b, l)); this->length += l; } }; /* A message from someone */ struct HTTPMessage final { std::map headers; std::map cookies; std::map get_data; std::map post_data; Anope::string content; }; class HTTPClient; class HTTPProvider; class HTTPPage : public virtual Base { Anope::string url; Anope::string content_type; public: HTTPPage(const Anope::string &u, const Anope::string &ct = "text/html") : url(u), content_type(ct) { } const Anope::string &GetURL() const { return this->url; } const Anope::string &GetContentType() const { return this->content_type; } /** Called when this page is requested * @param The server this page is on * @param The page name * @param The client requesting the page * @param The HTTP header sent from the client to request the page * @param The HTTP header that will be sent back to the client */ virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) = 0; }; class HTTPClient : public ClientSocket , public BinarySocket , public Base { protected: void WriteClient(const Anope::string &message) { BinarySocket::Write(message + "\r\n"); } public: HTTPClient(ListenSocket *l, int f, const sockaddrs &a) : ClientSocket(l, a), BinarySocket() { } virtual const Anope::string GetIP() { return this->clientaddr.addr(); } virtual void SendError(HTTPError err, const Anope::string &msg) = 0; virtual void SendReply(HTTPReply *) = 0; }; class HTTPProvider : public ListenSocket , public Service { Anope::string ip; unsigned short port; bool ssl; public: std::vector ext_ips; std::vector ext_headers; HTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, bool s) : ListenSocket(i, p, i.find(':') != Anope::string::npos), Service(c, "HTTPProvider", n), ip(i), port(p), ssl(s) { } const Anope::string &GetIP() const { return this->ip; } unsigned short GetPort() const { return this->port; } bool IsSSL() const { return this->ssl; } virtual bool RegisterPage(HTTPPage *page) = 0; virtual void UnregisterPage(HTTPPage *page) = 0; virtual HTTPPage *FindPage(const Anope::string &name) = 0; }; namespace HTTPUtils { inline Anope::string URLDecode(const Anope::string &url) { Anope::string decoded; for (unsigned i = 0; i < url.length(); ++i) { const char &c = url[i]; if (c == '%' && i + 2 < url.length()) { Anope::string dest; Anope::Unhex(url.substr(i + 1, 2), dest); decoded += dest; i += 2; } else if (c == '+') decoded += ' '; else decoded += c; } return decoded; } inline Anope::string URLEncode(const Anope::string &url) { Anope::string encoded; for (const auto c : url) { if (isalnum(c) || c == '.' || c == '-' || c == '*' || c == '_') encoded += c; else if (c == ' ') encoded += '+'; else encoded += "%" + Anope::Hex(c); } return encoded; } inline Anope::string Escape(const Anope::string &src) { Anope::string dst; for (const auto c : src) { switch (c) { case '<': dst += "<"; break; case '>': dst += ">"; break; case '"': dst += """; break; case '&': dst += "&"; break; default: dst += c; } } return dst; } }