1 /*
2  * COPYRIGHT:   See COPYING in the top level directory
3  * PROJECT:     ReactOS HTTP Daemon
4  * FILE:        httpd.cpp
5  * PURPOSE:     HTTP daemon
6  * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
7  * REVISIONS:
8  *   CSH  01/09/2000 Created
9  */
10 #include <debug.h>
11 #include <new>
12 #include <malloc.h>
13 #include <string.h>
14 #include <config.h>
15 #include <httpd.h>
16 #include <error.h>
17 
18 using namespace std;
19 
20 CHAR HttpMsg400[] = "<HEAD><TITLE>400 Bad Request</TITLE></HEAD>\n\r<BODY><H1>400 Bad Request</H1>\n\rThe request had bad syntax.<BR>\n\r</BODY>\n\r\n\r";
21 CHAR HttpMsg404[] = "<HEAD><TITLE>404 Not Found</TITLE></HEAD>\n\r<BODY><H1>404 Not Found</H1>\n\rThe requested URL was not found on this server.<BR>\n\r</BODY>\n\r\n\r";
22 CHAR HttpMsg405[] = "<HEAD><TITLE>405 Method Not Allowed</TITLE></HEAD>\n\r<BODY><H1>405 Method Not Allowed</H1>\n\rThe requested method is not supported on this server.<BR>\n\r</BODY>\n\r\n\r";
23 CHAR HttpMsg500[] = "<HEAD><TITLE>500 Internal Server Error</TITLE></HEAD>\n\r<BODY><H1>500 Internal Server Error</H1>\n\rAn internal error occurred.<BR>\n\r</BODY>\n\r\n\r";
24 CHAR HttpMsg501[] = "<HEAD><TITLE>501 Not Implemented</TITLE></HEAD>\n\r<BODY><H1>501 Not Implemented</H1>\n\rThe feature is not implemented.<BR>\n\r</BODY>\n\r\n\r";
25 
26 
27 // *************************** CHttpClient ***************************
28 
29 // Default constructor
CHttpClient()30 CHttpClient::CHttpClient()
31 {
32 }
33 
34 // Constructor with server socket as starter value
CHttpClient(CServerSocket * serversocket)35 CHttpClient::CHttpClient(CServerSocket *serversocket)
36 {
37 	ServerSocket = serversocket;
38 }
39 
40 // Split URIs into its parts (ie. |http://|www.host.com|/resource|?parameters|)
SplitUri(LPSTR lpsUri,LPSTR lpsHost,LPSTR lpsResource,LPSTR lpsParams)41 VOID CHttpClient::SplitUri(LPSTR lpsUri, LPSTR lpsHost, LPSTR lpsResource, LPSTR lpsParams)
42 {
43     LPSTR lpsPos;
44     LPSTR lpsStr;
45     UINT i;
46 
47 	strcpy(lpsHost, "");
48 	strcpy(lpsResource, "");
49 	strcpy(lpsParams, "");
50 
51     lpsPos = strstr(lpsUri, "://");
52     if (lpsPos != NULL)
53         lpsStr = &lpsPos[3];
54 	else
55 		lpsStr = lpsUri;
56 
57     lpsPos = strstr(lpsStr, "/");
58     if (lpsPos != NULL) {
59         strncat(lpsHost, lpsPos, lpsPos - lpsStr);
60         lpsStr = &lpsPos[1];
61 
62         lpsPos = strstr(lpsStr, "?");
63         if (lpsPos != NULL) {
64             strncat(lpsResource, lpsStr, lpsPos - lpsStr);
65             strcpy(lpsParams, &lpsPos[1]);
66         } else {
67             strcpy(lpsResource, lpsStr);
68             strcpy(lpsParams, "");
69         }
70 
71         // Replace "/" with "\"
72 		for (i = 0; i < strlen(lpsResource); i++) {
73             if (lpsResource[i] == '/')
74                 lpsResource[i] = '\\';
75         }
76     }
77 }
78 
79 // Split resource into its parts (ie. |/path/|filename|.extension|)
SplitResource(LPSTR lpsResource,LPSTR lpsPath,LPSTR lpsFilename,LPSTR lpsExtension)80 VOID CHttpClient::SplitResource(LPSTR lpsResource, LPSTR lpsPath, LPSTR lpsFilename, LPSTR lpsExtension)
81 {
82     INT i,len,fileptr,extptr;
83 
84 	strcpy(lpsPath, "");
85 	strcpy(lpsFilename, "");
86 	strcpy(lpsExtension, "");
87 
88 	len = strlen(lpsResource);
89 	if (len != 0) {
90 		if (lpsResource[len - 1] == '/') {
91 			// There is only a path
92 			strcpy(lpsPath, lpsResource);
93 		} else {
94 			// Find extension
95 			i = len - 1;
96 			while ((i >= 0) && (lpsResource[i] != '.')) i--;
97 			extptr = i;
98 			while ((i >= 0) && (lpsResource[i] != '/')) i--;
99 			if (i > 0) {
100 				// There is at least one directory in the path (besides root directory)
101 				fileptr = i + 1;
102 				strncat(lpsPath, lpsResource, fileptr);
103 			} else
104 				fileptr = 1;
105 
106 			// Get filename and possibly extension
107 			if (extptr != 0) {
108 				strncat(lpsFilename, &lpsResource[fileptr], extptr - fileptr);
109 				// Get extension
110 				strncat(lpsExtension, &lpsResource[extptr + 1], len - extptr - 1);
111 			} else
112 				strncat(lpsFilename, &lpsResource[fileptr], len - fileptr);
113 		}
114 	}
115 }
116 
117 // Process HTTP request
ProcessRequest()118 VOID CHttpClient::ProcessRequest()
119 {
120     CHAR sStr[255];
121     CHAR sHost[255];
122     CHAR sResource[255];
123     CHAR sParams[255];
124 
125     // Which method?
126     switch (Parser.nMethodNo) {
127 		case hmGET: {
128 			SplitUri(Parser.sUri, sHost, sResource, sParams);
129 
130 			// Default resource?
131 			if (strlen(sResource) == 0) {
132 				CIterator<LPSTR> *i = pConfiguration->GetDefaultResources()->CreateIterator();
133 
134 				// FIXME: All default resources should be tried
135 				// Iterate through all strings
136 				//for (i->First(); !i->IsDone(); i->Next())
137 				i->First();
138 				if (!i->IsDone()) {
139 					strcat(sResource, i->CurrentItem());
140 					delete i;
141 				} else {
142 					// File not found
143 					Report("404 Not Found", HttpMsg404);
144 					break;
145 				}
146 			}
147 			strcpy(sStr, pConfiguration->GetHttpBase());
148 			strcat(sStr, sResource);
149 			SendFile(sStr);
150 			break;
151 		}
152 		default: {
153 			// Method is not implemented
154 			Report("501 Not Implemented", HttpMsg501);
155 		}
156 	}
157 }
158 
159 // Send a file to socket
SendFile(LPSTR lpsFilename)160 VOID CHttpClient::SendFile(LPSTR lpsFilename)
161 {
162     CHAR str[255];
163     CHAR str2[32];
164     union BigNum {
165       //        unsigned __int64 Big;
166       unsigned long long Big;
167         struct {
168             DWORD Low;
169             DWORD High;
170         } u;
171     } nTotalBytes;
172 	DWORD nBytesToRead;
173 	DWORD nBytesRead;
174 	BOOL bStatus;
175 
176 	// Try to open file
177     hFile = CreateFileA(lpsFilename,
178         GENERIC_READ,               // Open for reading
179         FILE_SHARE_READ,            // Share for reading
180         NULL,                       // No security
181         OPEN_EXISTING,              // Existing file only
182         FILE_ATTRIBUTE_NORMAL,      // Normal file
183         NULL);                      // No attr. template
184     if (hFile == INVALID_HANDLE_VALUE) {
185         // File not found
186         Report("404 Not Found", HttpMsg404);
187         return;
188     }
189     // Get file size
190     nTotalBytes.u.Low = GetFileSize(hFile, &nTotalBytes.u.High);
191     if ((nTotalBytes.u.Low == 0xFFFFFFFF) && ((GetLastError()) != NO_ERROR)) {
192         // Internal server error
193 		Report("500 Internal Server Error", HttpMsg500);
194 		// Close file
195 		CloseHandle(hFile);
196         return;
197     }
198 
199 	// Determine buffer size
200 	if (nTotalBytes.Big < 65536)
201 		nBufferSize = 1024;
202 	else
203 		nBufferSize = 32768;
204 	// Allocate memory on heap
205 	lpsBuffer = (PCHAR) malloc(nBufferSize);
206 
207 	if (lpsBuffer == NULL) {
208 		// Internal server error
209 		Report("500 Internal Server Error", HttpMsg500);
210 		// Close file
211 		CloseHandle(hFile);
212 		return;
213 	}
214 
215     SendText("HTTP/1.1 200 OK");
216     SendText("Server: ROSHTTPD");
217     SendText("MIME-version: 1.0");
218     SendText("Content-Type: text/plain");
219     SendText("Accept-Ranges: bytes");
220     strcpy(str, "Content-Length: ");
221     _itoa(nTotalBytes.u.Low, str2, 10);
222     strcat(str, str2);
223     SendText(str);
224     SendText("");
225 	// Read and transmit file
226 	nTotalRead = 0;
227 	nFileSize = nTotalBytes.Big;
228 	bStop = FALSE;
229 
230 	fd_set wfds;
231 	FD_ZERO(&wfds);
232 	FD_SET(Socket, &wfds);
233 	do {
234 		MessageLoop();
235 
236 		if (nTotalRead + nBufferSize < nFileSize)
237 			nBytesToRead = nBufferSize;
238 		else nBytesToRead = nFileSize - nTotalRead;
239 
240 		bStatus = ReadFile(hFile, lpsBuffer, nBytesToRead, &nBytesRead, NULL);
241 		if (bStatus) {
242 			select(0, NULL, &wfds, NULL, NULL);
243 			bStatus = (Transmit(lpsBuffer, nBytesRead) == (INT)nBytesRead);
244 			nTotalRead += nBytesRead;
245 		}
246     } while ((!bStop) && (bStatus) && (nTotalRead < nFileSize));
247 
248 	if (bStatus)
249 		SendText("");
250 	else
251 		// We can't send an error message here as we are in the process of sending a file.
252 		// We have to terminate the connection instead
253 		Close();
254 
255 	// Free allocated memory
256 	free(lpsBuffer);
257 
258 	// Close file
259     CloseHandle(hFile);
260 }
261 
262 // Report something to client
Report(LPCSTR lpsCode,LPSTR lpsStr)263 VOID CHttpClient::Report(LPCSTR lpsCode, LPSTR lpsStr)
264 {
265     CHAR sTmp[128];
266     CHAR sTmp2[16];
267 
268     strcpy(sTmp, "HTTP/1.1 ");
269     strcat(sTmp, lpsCode);
270     SendText(sTmp);
271     SendText("Server: ROSHTTPD");
272     SendText("MIME-version: 1.0");
273     SendText("Content-Type: text/html");
274     SendText("Accept-Ranges: bytes");
275     strcpy(sTmp, "Content-Length: ");
276     if (lpsStr != NULL) {
277         _itoa(strlen(lpsStr), sTmp2, 10);
278         strcat(sTmp,  sTmp2);
279     } else
280         strcat(sTmp, "0");
281     SendText(sTmp);
282     SendText("");
283     if (lpsStr != NULL)
284         SendText(lpsStr);
285     SendText("");
286 }
287 
288 // OnRead event handler
OnRead()289 VOID CHttpClient::OnRead()
290 {
291 	LONG nCount;
292 
293 	nCount = Receive((LPSTR) &Parser.sBuffer[Parser.nHead],
294         sizeof(Parser.sBuffer) - Parser.nHead);
295 
296     Parser.nHead += nCount;
297 	if (Parser.nHead >= sizeof(Parser.sBuffer))
298 		Parser.nHead = 0;
299 
300     if (Parser.Complete()) {
301 		ProcessRequest();
302     }
303 
304 	if (Parser.bUnknownMethod) {
305 		// Method Not Allowed
306 		Report("405 Method Not Allowed", HttpMsg405);
307 		// Terminate connection
308 		Close();
309 	}
310 }
311 /*
312 // OnWrite event handler
313 VOID CHttpClient::OnWrite()
314 {
315 	DWORD nBytesToRead;
316 	DWORD nBytesRead;
317 
318 	OutputDebugString(TS("Can write\n"));
319 
320 	if (bSendingFile) {
321 		if (nTotalRead + nBufferSize < nFileSize)
322 			nBytesToRead = nBufferSize;
323 		else nBytesToRead = nFileSize - nTotalRead;
324 
325 		bError = ReadFile(hFile, Buffer, nBytesToRead, &nBytesRead, NULL);
326 		if (!bError) {
327 			Transmit(Buffer, nBytesRead);
328 			nTotalRead += nBytesRead;
329 		}
330 	}
331 }
332 */
333 // OnClose event handler
OnClose()334 VOID CHttpClient::OnClose()
335 {
336 	// Stop sending file if we are doing that now
337 	bStop = TRUE;
338 }
339 
340 
341 // ************************ CHttpClientThread ************************
342 
343 // Constructor with client socket as starter value
CHttpClientThread(LPCServerClientSocket lpSocket)344 CHttpClientThread::CHttpClientThread(LPCServerClientSocket lpSocket)
345 {
346 	ClientSocket = lpSocket;
347 }
348 
349 // Execute client thread code
Execute()350 VOID CHttpClientThread::Execute()
351 {
352     MSG Msg;
353 
354 	while (!Terminated()) {
355 		((  CHttpClient *) ClientSocket)->MessageLoop();
356         if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) {
357             switch (Msg.message) {
358                 case HTTPD_START: {
359 					// TODO: Start thread
360                     break;
361                 }
362                 case HTTPD_STOP: {
363 					// TODO: Stop thread
364                     break;
365                 }
366                 default:
367                     DispatchMessage(&Msg);
368             }
369 
370         }
371     }
372 
373 	if (ClientSocket != NULL) {
374 		delete ClientSocket;
375 		ClientSocket = NULL;
376 	}
377 }
378 
379 
380 // *************************** CHttpDaemon ***************************
381 
382 // Default constructor
CHttpDaemon()383 CHttpDaemon::CHttpDaemon()
384 {
385     State = hsStopped;
386 	Start();
387 }
388 
389 // Default destructor
~CHttpDaemon()390 CHttpDaemon::~CHttpDaemon()
391 {
392 	if (State==hsRunning)
393 		Stop();
394 }
395 
396 // Return daemon state
GetState() const397 HTTPdState CHttpDaemon::GetState() const
398 {
399 	return State;
400 }
401 
402 // Start HTTP daemon
Start()403 BOOL CHttpDaemon::Start()
404 {
405 	assert(State==hsStopped);
406 
407 	SetPort(pConfiguration->GetPort());
408 
409 	Open();
410 
411 	State = hsRunning;
412 
413     return TRUE;
414 }
415 
416 // Stop HTTP daemon
Stop()417 BOOL CHttpDaemon::Stop()
418 {
419 	assert(State==hsRunning);
420 
421 	Close();
422 
423 	State = hsStopped;
424 
425     return TRUE;
426 }
427 
428 // OnGetSocket event handler
OnGetSocket(LPCServerSocket lpServerSocket)429 LPCServerClientSocket CHttpDaemon::OnGetSocket(LPCServerSocket lpServerSocket)
430 {
431 	return new CHttpClient(lpServerSocket);
432 }
433 
434 // OnGetThread event handler
OnGetThread(LPCServerClientSocket lpSocket)435 LPCServerClientThread CHttpDaemon::OnGetThread(LPCServerClientSocket lpSocket)
436 {
437 	return new CHttpClientThread(lpSocket);
438 }
439 
440 // OnAccept event handler
OnAccept(LPCServerClientThread lpThread)441 VOID CHttpDaemon::OnAccept(LPCServerClientThread lpThread)
442 {
443 }
444 
445 
446 // ************************ CHttpDaemonThread ************************
447 
448 // Execute daemon thread code
Execute()449 VOID CHttpDaemonThread::Execute()
450 {
451 	MSG Msg;
452 
453 	try {
454 		Daemon = NULL;
455 		Daemon = new CHttpDaemon;
456 
457 		while (!Terminated()) {
458 			Daemon->MessageLoop();
459 			if (PeekMessage(&Msg, 0, 0, 0, PM_REMOVE) != 0) {
460 	            switch (Msg.message) {
461 		            case HTTPD_START: {
462 			            if (Daemon->GetState() == hsStopped)
463 				            Daemon->Start();
464                     break;
465 					}
466 					case HTTPD_STOP: {
467 	                    if (Daemon->GetState() == hsRunning)
468 							Daemon->Stop();
469                     break;
470 					}
471 					case HTTPD_SUSPEND: {
472 						if (Daemon->GetState() == hsRunning){}
473 							// FIXME: Suspend service
474                     break;
475 					}
476 					case HTTPD_RESUME: {
477 						if (Daemon->GetState() != hsSuspended){}
478 							// FIXME: Resume service
479                     break;
480 					}
481 					default:
482                         DispatchMessage(&Msg);
483 				}
484 			}
485 	    }
486 		delete Daemon;
487 	} catch (ESocket e) {
488 		ReportErrorStr(e.what());
489 	} catch	(bad_alloc&) {
490 		ReportErrorStr(TS("Insufficient resources."));
491 	}
492 }
493