1 /* 2 * 3 * (C) 2012-2020 Anope Team 4 * Contact us at team@anope.org 5 * 6 * Please read COPYING and README for further details. 7 */ 8 9 #ifndef ANOPE_HTTPD_H 10 #define ANOPE_HTTPD_H 11 12 enum HTTPError 13 { 14 HTTP_ERROR_OK = 200, 15 HTTP_FOUND = 302, 16 HTTP_BAD_REQUEST = 400, 17 HTTP_PAGE_NOT_FOUND = 404, 18 HTTP_NOT_SUPPORTED = 505 19 }; 20 21 /* A message to someone */ 22 struct HTTPReply 23 { 24 HTTPError error; 25 Anope::string content_type; 26 std::map<Anope::string, Anope::string, ci::less> headers; 27 typedef std::list<std::pair<Anope::string, Anope::string> > cookie; 28 std::vector<cookie> cookies; 29 HTTPReplyHTTPReply30 HTTPReply() : error(HTTP_ERROR_OK), length(0) { } 31 HTTPReplyHTTPReply32 HTTPReply(const HTTPReply& other) : error(other.error), length(other.length) 33 { 34 content_type = other.content_type; 35 headers = other.headers; 36 cookies = other.cookies; 37 38 for (unsigned i = 0; i < other.out.size(); ++i) 39 out.push_back(new Data(other.out[i]->buf, other.out[i]->len)); 40 } 41 ~HTTPReplyHTTPReply42 ~HTTPReply() 43 { 44 for (unsigned i = 0; i < out.size(); ++i) 45 delete out[i]; 46 out.clear(); 47 } 48 49 struct Data 50 { 51 char *buf; 52 size_t len; 53 DataHTTPReply::Data54 Data(const char *b, size_t l) 55 { 56 this->buf = new char[l]; 57 memcpy(this->buf, b, l); 58 this->len = l; 59 } 60 ~DataHTTPReply::Data61 ~Data() 62 { 63 delete [] buf; 64 } 65 }; 66 67 std::deque<Data *> out; 68 size_t length; 69 WriteHTTPReply70 void Write(const Anope::string &message) 71 { 72 this->out.push_back(new Data(message.c_str(), message.length())); 73 this->length += message.length(); 74 } 75 WriteHTTPReply76 void Write(const char *b, size_t l) 77 { 78 this->out.push_back(new Data(b, l)); 79 this->length += l; 80 } 81 }; 82 83 /* A message from soneone */ 84 struct HTTPMessage 85 { 86 std::map<Anope::string, Anope::string> headers; 87 std::map<Anope::string, Anope::string> cookies; 88 std::map<Anope::string, Anope::string> get_data; 89 std::map<Anope::string, Anope::string> post_data; 90 Anope::string content; 91 }; 92 93 class HTTPClient; 94 class HTTPProvider; 95 96 class HTTPPage : public Base 97 { 98 Anope::string url; 99 Anope::string content_type; 100 101 public: url(u)102 HTTPPage(const Anope::string &u, const Anope::string &ct = "text/html") : url(u), content_type(ct) { } 103 GetURL()104 const Anope::string &GetURL() const { return this->url; } 105 GetContentType()106 const Anope::string &GetContentType() const { return this->content_type; } 107 108 /** Called when this page is requested 109 * @param The server this page is on 110 * @param The page name 111 * @param The client requesting the page 112 * @param The HTTP header sent from the client to request the page 113 * @param The HTTP header that will be sent back to the client 114 */ 115 virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) = 0; 116 }; 117 118 class HTTPClient : public ClientSocket, public BinarySocket, public Base 119 { 120 protected: WriteClient(const Anope::string & message)121 void WriteClient(const Anope::string &message) 122 { 123 BinarySocket::Write(message + "\r\n"); 124 } 125 126 public: HTTPClient(ListenSocket * l,int f,const sockaddrs & a)127 HTTPClient(ListenSocket *l, int f, const sockaddrs &a) : ClientSocket(l, a), BinarySocket() { } 128 GetIP()129 virtual const Anope::string GetIP() 130 { 131 return this->clientaddr.addr(); 132 } 133 134 virtual void SendError(HTTPError err, const Anope::string &msg) = 0; 135 virtual void SendReply(HTTPReply *) = 0; 136 }; 137 138 class HTTPProvider : public ListenSocket, public Service 139 { 140 Anope::string ip; 141 unsigned short port; 142 bool ssl; 143 public: 144 std::vector<Anope::string> ext_ips; 145 std::vector<Anope::string> ext_headers; 146 HTTPProvider(Module * c,const Anope::string & n,const Anope::string & i,const unsigned short p,bool s)147 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) { } 148 GetIP()149 const Anope::string &GetIP() const 150 { 151 return this->ip; 152 } 153 GetPort()154 unsigned short GetPort() const 155 { 156 return this->port; 157 } 158 IsSSL()159 bool IsSSL() const 160 { 161 return this->ssl; 162 } 163 164 virtual bool RegisterPage(HTTPPage *page) = 0; 165 virtual void UnregisterPage(HTTPPage *page) = 0; 166 virtual HTTPPage* FindPage(const Anope::string &name) = 0; 167 }; 168 169 namespace HTTPUtils 170 { URLDecode(const Anope::string & url)171 inline Anope::string URLDecode(const Anope::string &url) 172 { 173 Anope::string decoded; 174 175 for (unsigned i = 0; i < url.length(); ++i) 176 { 177 const char& c = url[i]; 178 179 if (c == '%' && i + 2 < url.length()) 180 { 181 Anope::string dest; 182 Anope::Unhex(url.substr(i + 1, 2), dest); 183 decoded += dest; 184 i += 2; 185 } 186 else if (c == '+') 187 decoded += ' '; 188 else 189 decoded += c; 190 } 191 192 return decoded; 193 } 194 URLEncode(const Anope::string & url)195 inline Anope::string URLEncode(const Anope::string &url) 196 { 197 Anope::string encoded; 198 199 for (unsigned i = 0; i < url.length(); ++i) 200 { 201 const char& c = url[i]; 202 203 if (isalnum(c) || c == '.' || c == '-' || c == '*' || c == '_') 204 encoded += c; 205 else if (c == ' ') 206 encoded += '+'; 207 else 208 encoded += "%" + Anope::Hex(c); 209 } 210 211 return encoded; 212 } 213 Escape(const Anope::string & src)214 inline Anope::string Escape(const Anope::string &src) 215 { 216 Anope::string dst; 217 218 for (unsigned i = 0; i < src.length(); ++i) 219 { 220 switch (src[i]) 221 { 222 case '<': 223 dst += "<"; 224 break; 225 case '>': 226 dst += ">"; 227 break; 228 case '"': 229 dst += """; 230 break; 231 case '&': 232 dst += "&"; 233 break; 234 default: 235 dst += src[i]; 236 } 237 } 238 239 return dst; 240 } 241 } 242 243 #endif // ANOPE_HTTPD_H 244