ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 349
Committed: 2004-12-16T18:15:34-08:00 (20 years, 6 months ago) by douglas
File size: 6304 byte(s)
Log Message:
Horribly broken, including parts of menes.

File Contents

# Content
1 // Smersh
2 //
3 // Douglas Thrift
4 //
5 // $Id$
6
7 #include "Daemon.hpp"
8
9 string Daemon::crlf("\r\n");
10
11 void Daemon::serve(Daemon* self)
12 {
13 ext::StackReference<api::TcpSocket> server;
14
15 server.SetAddress(api::InternetAddress(api::InternetAddress::Any, port));
16 server.Listen(50);
17
18 while (true)
19 {
20 Client* client(new Client(server));
21 api::Thread thread(etl::BindAll(&Daemon::handle, self, client));
22 }
23 }
24
25 Daemon::Status Daemon::request(istream& sin, Environment& env, ostream& post,
26 ostream& log)
27 {
28 string line;
29 Matcher request("^([A-Z]+) (.*?)(\\?.+)? HTTP/(\\d+)\\.(\\d+)$");
30
31 getline(sin, line);
32
33 log << '"' << line << "\" ";
34
35 env.put("HTTP_REFERER=-");
36 env.put("HTTP_USER_AGENT=-");
37
38 if (line == request)
39 {
40 if (lexical_cast<unsigned>(request[4]) > 1) return version;
41
42 Matcher method("^GET|HEAD|POST$");
43
44 if (request[1] != method) return notImplemented;
45
46 env.set("REQUEST_METHOD", method[0]);
47 env.set("REQUEST_URI", request[2] + request[3]);
48
49 if (!request[3].IsEmpty()) env.set("QUERY_STRING", request[3].Substring(1));
50
51 headers(sin, env);
52
53 if (env.get("HTTP_HOST").empty()) return bad;
54 if (method[0] == "POST") return message(sin, env, post);
55
56 return ok;
57 }
58
59 return bad;
60 }
61
62 void Daemon::response(ostream& sout, Status status)
63 {
64 sout << "HTTP/1.1 " << status << ' ' << reason(status) << crlf << "Date: "
65 << date() << crlf << "Server: " << server() << crlf
66 << "Connection: close" << crlf;
67 }
68
69 string Daemon::reason(Status status)
70 {
71 ostringstream sout;
72
73 switch (status)
74 {
75 case ok:
76 sout << "OK";
77 break;
78 case found:
79 sout << "Found";
80 break;
81 case seeOther:
82 sout << "See Other";
83 break;
84 case bad:
85 sout << "Bad Request";
86 break;
87 case notFound:
88 sout << "Not Found";
89 break;
90 case lengthRequired:
91 sout << "Length Required";
92 break;
93 case mediaType:
94 sout << "Unsupported Media Type";
95 break;
96 case serverError:
97 sout << "Internal Server Error";
98 break;
99 case notImplemented:
100 sout << "Not Implemented";
101 break;
102 case version:
103 sout << "HTTP Version not supported";
104 }
105
106 return sout.str();
107 }
108
109 string Daemon::server()
110 {
111 utsname system;
112
113 uname(&system);
114
115 return string("Smersh/0.9 (") + system.sysname + ')';
116 }
117
118 string Daemon::server(const Environment& env)
119 {
120 ostringstream server;
121 string port(env.get("SERVER_PORT"));
122
123 server << this->server() << " Server at " << env.get("SERVER_NAME") << " Po"
124 << "rt " << (port.empty() ? lexical_cast<string>(this->port) : port);
125
126 return server.str();
127 }
128
129 streamsize Daemon::error(ostream& sout, Status status, const Environment& env)
130 {
131 string reason(this->reason(status));
132 ostringstream error;
133
134 error << "<html><head><title>" << status << ' ' << reason << "</title></hea"
135 << "d><body><h1>" << reason << "</h1><p>Mistakes were made, deal with t"
136 << "hem.</p><hr /><address>" << server(env) << "</address></body></html"
137 << ">\r\n";
138 sout << "Content-Length: " << error.str().length() << crlf
139 << "Content-Type: text/html; charset=UTF-8\r\n\r\n" << error.str();
140
141 return error.str().size();
142 }
143
144 string Daemon::date(bool log)
145 {
146 time_t now(time(NULL));
147 tm time;
148
149 if (log) localtime_r(&now, &time); else gmtime_r(&now, &time);
150
151 const char* format(log ? "[%m/%d/%Y:%T %z]" : "%a, %d %b %Y %T GMT");
152 char when[log ? 29 : 30];
153
154 strftime(when, log ? 29 : 30, format, &time);
155
156 return when;
157 }
158
159 int Daemon::handle(Client* client)
160 {
161 ios::ToIoStream sio(&client->socket, &client->socket);
162 Environment env;
163 stringstream post;
164 ostringstream log;
165 Status code(request(sio, env, post, log));
166
167 if (env.get("REQUEST_URI") == "/favicon.ico") code = notFound;
168
169 response(sio, code);
170
171 bool head(env.get("REQUEST_METHOD") == "HEAD");
172 streamsize sent(0);
173
174 if (code == ok && env.get("REQUEST_URI") == "/robots.txt")
175 {
176 sio << "Content-Length: 28\r\nContent-Type: text/plain; charset=UTF-8\r"
177 << "\n\r\n";
178
179 if (!head) { sio << "User-agent: *\r\nDisallow: /\r\n"; sent = 28; }
180 }
181 else if (code == ok && !head)
182 {
183 ostringstream content;
184 Smersh smersh(post, content, env);
185
186 sio << "Content-Length: " << content.str().length() << crlf
187 << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
188
189 sio.write(content.str().data(), content.str().size());
190
191 sent = content.str().size();
192 }
193 else if (head) sio << "Content-Type: text/html; charset=UTF-8\r\n\r\n";
194 else sent = error(sio, code, env);
195
196 ofstream fout(this->log.c_str(), ios_base::app);
197
198 fout << inet_ntoa(client->ip->sin_addr) << " - - " << date(true) << ' '
199 << log.str() << code << ' ' << lexical_cast<string>(sent) << " \""
200 << env.get("HTTP_REFERER") << "\" \"" << env.get("HTTP_USER_AGENT")
201 << "\"\n";
202 sio << flush;
203
204 client->socket.ShutdownWrite();
205
206 delete client;
207
208 return 0;
209 }
210
211 void Daemon::headers(istream& sin, Environment& env)
212 {
213 do
214 {
215 string line;
216
217 getline(sin, line);
218
219 if (line.empty()) break;
220
221 istringstream input(line);
222 string name, value;
223
224 std::getline(input, name, ':');
225 getline(input, value);
226
227 for (char next(sin.peek()); next == ' ' || next == '\t'; next =
228 sin.peek())
229 {
230 getline(sin, line);
231
232 value += ' ' + line;
233 }
234
235 Matcher matcher("^\\s*(.+)\\s*$");
236
237 if (name == "Content-Length")
238 {
239 if (value == matcher) env.set("CONTENT_LENGTH", matcher[1]);
240 }
241 else if (name == "Content-Type")
242 {
243 if (value == matcher) env.set("CONTENT_TYPE", matcher[1]);
244 }
245 else if (name == "Host")
246 {
247 Matcher matcher("^\\s*(.+?)(:[0-9]+)?\\s*$");
248
249 if (value == matcher)
250 {
251 bool port(matcher.size() > 2);
252
253 env.set("HTTP_HOST", matcher[1] + (port ? matcher[2] : ""));
254 env.set("SERVER_NAME", matcher[1]);
255
256 if (port) env.set("SERVER_PORT", matcher[2].Substring(1));
257 }
258 }
259 else if (name == "Referer")
260 {
261 if (value == matcher) env.set("HTTP_REFERER", matcher[1]);
262 }
263 else if (name == "User-Agent")
264 {
265 if (value == matcher) env.set("HTTP_USER_AGENT", matcher[1]);
266 }
267 }
268 while (sin.good());
269 }
270
271 Daemon::Status Daemon::message(istream& sin, Environment& env, ostream& post)
272 {
273 string contentLength(env.get("CONTENT_LENGTH"));
274
275 if (env.get("CONTENT_TYPE") != "application/x-www-form-urlencoded") return
276 mediaType;
277 if (contentLength.empty()) return lengthRequired;
278
279 streamsize length(lexical_cast<streamsize>(contentLength));
280 char* content(new char[length]);
281
282 sin.read(content, length);
283 post.write(content, length);
284
285 delete [] content;
286
287 return ok;
288 }

Properties

Name Value
svn:eol-style native
svn:keywords Id