ViewVC Help
View File | Revision Log | Show Annotations | Download File | View Changeset | Root Listing
root/repos/Smersh/Daemon.cpp
Revision: 178
Committed: 2004-06-29T13:15:51-07:00 (21 years ago) by Douglas Thrift
File size: 5689 byte(s)
Log Message:
Works better, but dies.

File Contents

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

Properties

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