Anope IRC Services  Version 2.0
m_httpd.cpp
Go to the documentation of this file.
1 /*
2  * (C) 2003-2014 Anope Team
3  * Contact us at team@anope.org
4  *
5  * Please read COPYING and README for further details.
6  */
7 
8 #include "module.h"
9 #include "modules/httpd.h"
10 #include "modules/ssl.h"
11 
13 {
14  char timebuf[64];
15  struct tm *tm = localtime(&Anope::CurTime);
16  strftime(timebuf, sizeof(timebuf), "%a, %d %b %Y %H:%M:%S %Z", tm);
17  return timebuf;
18 }
19 
21 {
22  switch (err)
23  {
24  case HTTP_ERROR_OK:
25  return "200 OK";
26  case HTTP_FOUND:
27  return "302 Found";
28  case HTTP_BAD_REQUEST:
29  return "400 Bad Request";
31  return "404 Not Found";
32  case HTTP_NOT_SUPPORTED:
33  return "505 HTTP Version Not Supported";
34  }
35 
36  return "501 Not Implemented";
37 }
38 
39 class MyHTTPClient : public HTTPClient
40 {
47 
48  unsigned content_length;
49 
50  enum
51  {
55  } action;
56 
57  void Serve()
58  {
59  if (this->served)
60  return;
61  this->served = true;
62 
63  if (!this->page)
64  {
65  this->SendError(HTTP_PAGE_NOT_FOUND, "Page not found");
66  return;
67  }
68 
69  if (this->ip == this->provider->ext_ip)
70  {
71  for (unsigned i = 0; i < this->provider->ext_headers.size(); ++i)
72  {
73  const Anope::string &token = this->provider->ext_headers[i];
74 
75  if (this->message.headers.count(token))
76  {
77  this->ip = this->message.headers[token];
78  Log(LOG_DEBUG, "httpd") << "m_httpd: IP for connection " << this->GetFD() << " changed to " << this->ip;
79  break;
80  }
81  }
82  }
83 
84  Log(LOG_DEBUG, "httpd") << "m_httpd: Serving page " << this->page_name << " to " << this->ip;
85 
86  HTTPReply reply;
87  reply.content_type = this->page->GetContentType();
88 
89  if (this->page->OnRequest(this->provider, this->page_name, this, this->message, reply))
90  this->SendReply(&reply);
91  }
92 
93  public:
94  time_t created;
95 
96  MyHTTPClient(HTTPProvider *l, int f, const sockaddrs &a) : Socket(f, l->IsIPv6()), HTTPClient(l, f, a), provider(l), header_done(false), served(false), ip(a.addr()), content_length(0), action(ACTION_NONE), created(Anope::CurTime)
97  {
98  Log(LOG_DEBUG, "httpd") << "Accepted connection " << f << " from " << a.addr();
99  }
100 
102  {
103  Log(LOG_DEBUG, "httpd") << "Closing connection " << this->GetFD() << " from " << this->ip;
104  }
105 
106  /* Close connection once all data is written */
108  {
109  return !BinarySocket::ProcessWrite() || this->write_buffer.empty() ? false : true;
110  }
111 
113  {
114  return this->ip;
115  }
116 
117  bool Read(const char *buffer, size_t l) anope_override
118  {
119  message.content.append(buffer, l);
120 
121  for (size_t nl; !this->header_done && (nl = message.content.find('\n')) != Anope::string::npos;)
122  {
123  Anope::string token = message.content.substr(0, nl).trim();
124  message.content = message.content.substr(nl + 1);
125 
126  if (token.empty())
127  this->header_done = true;
128  else
129  this->Read(token);
130  }
131 
132  if (!this->header_done)
133  return true;
134 
135  if (this->message.content.length() >= this->content_length)
136  {
137  sepstream sep(this->message.content, '&');
138  Anope::string token;
139 
140  while (sep.GetToken(token))
141  {
142  size_t sz = token.find('=');
143  if (sz == Anope::string::npos || !sz || sz + 1 >= token.length())
144  continue;
145  this->message.post_data[token.substr(0, sz)] = HTTPUtils::URLDecode(token.substr(sz + 1));
146  Log(LOG_DEBUG_2) << "HTTP POST from " << this->clientaddr.addr() << ": " << token.substr(0, sz) << ": " << this->message.post_data[token.substr(0, sz)];
147  }
148 
149  this->Serve();
150  }
151 
152  return true;
153  }
154 
155  bool Read(const Anope::string &buf)
156  {
157  Log(LOG_DEBUG_2) << "HTTP from " << this->clientaddr.addr() << ": " << buf;
158 
159  if (this->action == ACTION_NONE)
160  {
161  std::vector<Anope::string> params;
162  spacesepstream(buf).GetTokens(params);
163 
164  if (params.empty() || (params[0] != "GET" && params[0] != "POST"))
165  {
166  this->SendError(HTTP_BAD_REQUEST, "Unknown operation");
167  return true;
168  }
169 
170  if (params.size() != 3)
171  {
172  this->SendError(HTTP_BAD_REQUEST, "Invalid parameters");
173  return true;
174  }
175 
176  if (params[0] == "GET")
177  this->action = ACTION_GET;
178  else if (params[0] == "POST")
179  this->action = ACTION_POST;
180 
181  Anope::string targ = params[1];
182  size_t q = targ.find('?');
183  if (q != Anope::string::npos)
184  {
185  sepstream sep(targ.substr(q + 1), '&');
186  targ = targ.substr(0, q);
187 
188  Anope::string token;
189  while (sep.GetToken(token))
190  {
191  size_t sz = token.find('=');
192  if (sz == Anope::string::npos || !sz || sz + 1 >= token.length())
193  continue;
194  this->message.get_data[token.substr(0, sz)] = HTTPUtils::URLDecode(token.substr(sz + 1));
195  }
196  }
197 
198  this->page = this->provider->FindPage(targ);
199  this->page_name = targ;
200  }
201  else if (buf.find("Cookie: ") == 0)
202  {
203  spacesepstream sep(buf.substr(8));
204  Anope::string token;
205 
206  while (sep.GetToken(token))
207  {
208  size_t sz = token.find('=');
209  if (sz == Anope::string::npos || !sz || sz + 1 >= token.length())
210  continue;
211  size_t end = token.length() - (sz + 1);
212  if (!sep.StreamEnd())
213  --end; // Remove trailing ;
214  this->message.cookies[token.substr(0, sz)] = token.substr(sz + 1, end);
215  }
216  }
217  else if (buf.find("Content-Length: ") == 0)
218  {
219  try
220  {
221  this->content_length = convertTo<unsigned>(buf.substr(16));
222  }
223  catch (const ConvertException &ex) { }
224  }
225  else if (buf.find(':') != Anope::string::npos)
226  {
227  size_t sz = buf.find(':');
228  if (sz + 2 < buf.length())
229  this->message.headers[buf.substr(0, sz)] = buf.substr(sz + 2);
230  }
231 
232  return true;
233  }
234 
236  {
237  HTTPReply h;
238 
239  h.error = err;
240 
241  h.Write(msg);
242 
243  this->SendReply(&h);
244  }
245 
247  {
248  this->WriteClient("HTTP/1.1 " + GetStatusFromCode(msg->error));
249  this->WriteClient("Date: " + BuildDate());
250  this->WriteClient("Server: Anope-" + Anope::VersionShort());
251  if (msg->content_type.empty())
252  this->WriteClient("Content-Type: text/html");
253  else
254  this->WriteClient("Content-Type: " + msg->content_type);
255  this->WriteClient("Content-Length: " + stringify(msg->length));
256 
257  for (unsigned i = 0; i < msg->cookies.size(); ++i)
258  {
259  Anope::string buf = "Set-Cookie:";
260 
261  for (HTTPReply::cookie::iterator it = msg->cookies[i].begin(), it_end = msg->cookies[i].end(); it != it_end; ++it)
262  buf += " " + it->first + "=" + it->second + ";";
263 
264  buf.erase(buf.length() - 1);
265 
266  this->WriteClient(buf);
267  }
268 
269  typedef std::map<Anope::string, Anope::string> map;
270  for (map::iterator it = msg->headers.begin(), it_end = msg->headers.end(); it != it_end; ++it)
271  this->WriteClient(it->first + ": " + it->second);
272 
273  this->WriteClient("Connection: Close");
274  this->WriteClient("");
275 
276  for (unsigned i = 0; i < msg->out.size(); ++i)
277  {
278  HTTPReply::Data* d = msg->out[i];
279 
280  this->Write(d->buf, d->len);
281 
282  delete d;
283  }
284 
285  msg->out.clear();
286  }
287 };
288 
289 class MyHTTPProvider : public HTTPProvider, public Timer
290 {
291  int timeout;
292  std::map<Anope::string, HTTPPage *> pages;
293  std::list<Reference<MyHTTPClient> > clients;
294 
295  public:
296  MyHTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, const int t, bool s) : Socket(-1, i.find(':') != Anope::string::npos), HTTPProvider(c, n, i, p, s), Timer(c, 10, Anope::CurTime, true), timeout(t) { }
297 
298  void Tick(time_t) anope_override
299  {
300  while (!this->clients.empty())
301  {
302  Reference<MyHTTPClient>& c = this->clients.front();
303  if (c && c->created + this->timeout >= Anope::CurTime)
304  break;
305 
306  delete c;
307  this->clients.pop_front();
308  }
309  }
310 
312  {
313  MyHTTPClient *c = new MyHTTPClient(this, fd, addr);
314  this->clients.push_back(c);
315  return c;
316  }
317 
319  {
320  return this->pages.insert(std::make_pair(page->GetURL(), page)).second;
321  }
322 
324  {
325  this->pages.erase(page->GetURL());
326  }
327 
329  {
330  if (this->pages.count(pname) == 0)
331  return NULL;
332  return this->pages[pname];
333  }
334 };
335 
336 class HTTPD : public Module
337 {
339  std::map<Anope::string, MyHTTPProvider *> providers;
340  public:
341  HTTPD(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), sslref("SSLService", "ssl")
342  {
343 
344  }
345 
347  {
348  for (std::map<int, Socket *>::const_iterator it = SocketEngine::Sockets.begin(), it_end = SocketEngine::Sockets.end(); it != it_end;)
349  {
350  Socket *s = it->second;
351  ++it;
352 
353  if (dynamic_cast<MyHTTPProvider *>(s) || dynamic_cast<MyHTTPClient *>(s))
354  delete s;
355  }
356 
357  this->providers.clear();
358  }
359 
361  {
362  Configuration::Block *conf = config->GetModule(this);
363  std::set<Anope::string> existing;
364 
365  for (int i = 0; i < conf->CountBlock("httpd"); ++i)
366  {
367  Configuration::Block *block = conf->GetBlock("httpd", i);
368 
369 
370  const Anope::string &hname = block->Get<const Anope::string>("name", "httpd/main");
371  existing.insert(hname);
372 
373  Anope::string ip = block->Get<const Anope::string>("ip");
374  int port = block->Get<int>("port", "8080");
375  int timeout = block->Get<int>("timeout", "30");
376  bool ssl = block->Get<bool>("ssl", "no");
377  Anope::string ext_ip = block->Get<const Anope::string>("extforward_ip");
378  Anope::string ext_header = block->Get<const Anope::string>("extforward_header");
379 
380  if (ip.empty())
381  {
382  Log(this) << "You must configure a bind IP for HTTP server " << hname;
383  continue;
384  }
385  else if (port <= 0 || port > 65535)
386  {
387  Log(this) << "You must configure a (valid) listen port for HTTP server " << hname;
388  continue;
389  }
390 
391  MyHTTPProvider *p;
392  if (this->providers.count(hname) == 0)
393  {
394  try
395  {
396  p = new MyHTTPProvider(this, hname, ip, port, timeout, ssl);
397  if (ssl && sslref)
398  sslref->Init(p);
399  }
400  catch (const SocketException &ex)
401  {
402  Log(this) << "Unable to create HTTP server " << hname << ": " << ex.GetReason();
403  continue;
404  }
405  this->providers[hname] = p;
406 
407  Log(this) << "Created HTTP server " << hname;
408  }
409  else
410  {
411  p = this->providers[hname];
412 
413  if (p->GetIP() != ip || p->GetPort() != port)
414  {
415  delete p;
416  this->providers.erase(hname);
417 
418  Log(this) << "Changing HTTP server " << hname << " to " << ip << ":" << port;
419 
420  try
421  {
422  p = new MyHTTPProvider(this, hname, ip, port, timeout, ssl);
423  if (ssl && sslref)
424  sslref->Init(p);
425  }
426  catch (const SocketException &ex)
427  {
428  Log(this) << "Unable to create HTTP server " << hname << ": " << ex.GetReason();
429  continue;
430  }
431 
432  this->providers[hname] = p;
433  }
434  }
435 
436 
437  p->ext_ip = ext_ip;
438  spacesepstream(ext_header).GetTokens(p->ext_headers);
439  }
440 
441  for (std::map<Anope::string, MyHTTPProvider *>::iterator it = this->providers.begin(), it_end = this->providers.end(); it != it_end;)
442  {
443  HTTPProvider *p = it->second;
444  ++it;
445 
446  if (existing.count(p->name) == 0)
447  {
448  Log(this) << "Removing HTTP server " << p->name;
449  this->providers.erase(p->name);
450  delete p;
451  }
452  }
453  }
454 
456  {
457  for (std::map<Anope::string, MyHTTPProvider *>::iterator it = this->providers.begin(), it_end = this->providers.end(); it != it_end; ++it)
458  {
459  MyHTTPProvider *p = it->second;
460 
461  if (p->IsSSL() && sslref)
462  try
463  {
464  sslref->Init(p);
465  }
466  catch (const CoreException &) { } // Throws on reinitialization
467  }
468  }
469 };
470 
bool Read(const Anope::string &buf)
Definition: m_httpd.cpp:155
HTTPError
Definition: httpd.h:4
void SendReply(HTTPReply *msg) anope_override
Definition: m_httpd.cpp:246
void OnModuleLoad(User *u, Module *m) anope_override
Definition: m_httpd.cpp:455
int CountBlock(const Anope::string &name)
Definition: config.cpp:35
unsigned short GetPort() const
Definition: httpd.h:146
std::map< Anope::string, Anope::string > headers
Definition: httpd.h:78
Anope::string content_type
Definition: httpd.h:17
Anope::string content
Definition: httpd.h:82
Definition: timers.h:18
std::map< Anope::string, MyHTTPProvider * > providers
Definition: m_httpd.cpp:339
virtual void Write(const char *buffer, size_t l)
char * buf
Definition: httpd.h:43
string & append(const string &s)
Definition: anope.h:144
HTTPPage * FindPage(const Anope::string &pname)
Definition: m_httpd.cpp:328
bool header_done
Definition: m_httpd.cpp:43
bool ProcessWrite() anope_override
Definition: m_httpd.cpp:107
Anope::string addr() const
Definition: sockets.cpp:73
bool IsIPv6() const
Definition: sockets.cpp:479
const Anope::string & GetIP() const
Definition: httpd.h:141
void GetTokens(T &token)
Definition: anope.h:587
MyHTTPProvider(Module *c, const Anope::string &n, const Anope::string &i, const unsigned short p, const int t, bool s)
Definition: m_httpd.cpp:296
Definition: users.h:34
static std::map< int, Socket * > Sockets
Definition: socketengine.h:24
bool IsSSL() const
Definition: httpd.h:151
static Anope::string BuildDate()
Definition: m_httpd.cpp:12
enum MyHTTPClient::@7 action
sockaddrs clientaddr
Definition: sockets.h:426
void Tick(time_t) anope_override
Definition: m_httpd.cpp:298
void WriteClient(const Anope::string &message)
Definition: httpd.h:113
bool served
Definition: m_httpd.cpp:43
CoreExport time_t CurTime
Definition: main.cpp:41
ClientSocket * OnAccept(int fd, const sockaddrs &addr) anope_override
Definition: m_httpd.cpp:311
int GetFD() const
Definition: sockets.cpp:474
iterator erase(const iterator &i)
Definition: anope.h:155
unsigned content_length
Definition: m_httpd.cpp:48
string substr(size_type pos=0, size_type n=npos) const
Definition: anope.h:277
HTTPMessage message
Definition: m_httpd.cpp:42
bool ProcessWrite() anope_override
size_type length() const
Definition: anope.h:131
static Anope::string GetStatusFromCode(HTTPError err)
Definition: m_httpd.cpp:20
Block * GetBlock(const Anope::string &name, int num=0)
Definition: config.cpp:43
bool Read(const char *buffer, size_t l) anope_override
Definition: m_httpd.cpp:117
size_t len
Definition: httpd.h:44
static const size_type npos
Definition: anope.h:44
void SendError(HTTPError err, const Anope::string &msg) anope_override
Definition: m_httpd.cpp:235
std::deque< DataBlock * > write_buffer
Definition: sockets.h:331
std::list< Reference< MyHTTPClient > > clients
Definition: m_httpd.cpp:293
Anope::string page_name
Definition: m_httpd.cpp:44
time_t created
Definition: m_httpd.cpp:94
CoreExport string VersionShort()
Definition: misc.cpp:634
virtual HTTPPage * FindPage(const Anope::string &name)=0
std::map< Anope::string, Anope::string > cookies
Definition: httpd.h:79
~HTTPD()
Definition: m_httpd.cpp:346
string & trim(const Anope::string &what=" \t\r\n")
Definition: anope.h:177
void UnregisterPage(HTTPPage *page) anope_override
Definition: m_httpd.cpp:323
bool RegisterPage(HTTPPage *page) anope_override
Definition: m_httpd.cpp:318
HTTPError error
Definition: httpd.h:16
#define anope_override
Definition: services.h:56
bool empty() const
Definition: anope.h:126
Anope::string ip
Definition: m_httpd.cpp:46
const Anope::string & GetContentType() const
Definition: httpd.h:98
HTTPProvider * provider
Definition: m_httpd.cpp:41
std::map< Anope::string, HTTPPage * > pages
Definition: m_httpd.cpp:292
std::basic_string< char, ci_char_traits, std::allocator< char > > string
Definition: hashcomp.h:133
#define MODULE_INIT(x)
Definition: modules.h:45
static Timer * timeout
Definition: os_defcon.cpp:106
Anope::string ext_ip
Definition: httpd.h:136
void OnReload(Configuration::Conf *config) anope_override
Definition: m_httpd.cpp:360
Anope::string stringify(const T &x)
Definition: anope.h:710
HTTPD(const Anope::string &modname, const Anope::string &creator)
Definition: m_httpd.cpp:341
std::map< Anope::string, Anope::string > post_data
Definition: httpd.h:81
ServiceReference< SSLService > sslref
Definition: m_httpd.cpp:338
void Write(const Anope::string &message)
Definition: httpd.h:62
Anope::string name
Definition: service.h:88
void Serve()
Definition: m_httpd.cpp:57
Reference< HTTPPage > page
Definition: m_httpd.cpp:45
bool GetToken(Anope::string &token)
Definition: hashcomp.cpp:99
const Anope::string GetIP() anope_override
Definition: m_httpd.cpp:112
MyHTTPClient(HTTPProvider *l, int f, const sockaddrs &a)
Definition: m_httpd.cpp:96
virtual const Anope::string & GetReason() const
Definition: anope.h:672
virtual bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &)=0
Definition: logger.h:53
T Get(const Anope::string &tag)
Definition: config.h:44
Anope::string URLDecode(const Anope::string &url)
Definition: httpd.h:163
Definition: httpd.h:88
std::map< Anope::string, Anope::string > get_data
Definition: httpd.h:80
CoreExport Anope::string strftime(time_t t, const NickCore *nc=NULL, bool short_output=false)
Definition: misc.cpp:356
std::vector< Anope::string > ext_headers
Definition: httpd.h:137
size_type find(const string &_str, size_type pos=0) const
Definition: anope.h:192
Definition: anope.h:20
Definition: modules.h:163