Fix for windows
[webserver.git] / http.c
1 #include <malloc.h>
2 #include <string.h>
3 #include <time.h>
4
5 #include "debug.h"
6 #include "file.h"
7
8 #include "http.h"
9
10 #define BUFFER_SIZE 128
11
12 #define HTTP_VERSION "HTTP/1.0"
13 #define SERVER_NAME "Webserver/0.0.1"
14
15 char *codes[15] = {
16 "200 OK",
17 "201 Created",
18 "202 Accepted",
19 "204 No Content",
20 "301 Moved Permanently",
21 "302 Moved Temporarily",
22 "304 Not Modified",
23 "400 Bad Request",
24 "401 Unauthorized",
25 "403 Forbidden",
26 "404 Not Found",
27 "500 Internal Server Error",
28 "501 Not Implemented",
29 "502 Bad Gateway",
30 "503 Service Unavailable"};
31
32 typedef enum {
33 c200 = 0, c201, c202, c204,
34 c301, c302, c304, c400,
35 c401, c403, c404, c500,
36 c501, c502, c503
37 } code_t;
38
39 typedef enum {
40 not_supported_e = 0, get_e, head_e, post_e
41 } method_t;
42
43 typedef struct {
44 char *allow;
45 char *authorization;
46 char *content_encoding;
47 char *content_length;
48 char *content_type;
49 char *date;
50 char *expires;
51 char *from;
52 char *if_modified_since;
53 char *last_modified;
54 char *location;
55 char *pragma;
56 char *referer;
57 char *server;
58 char *user_agent;
59 char *www_authenticate;
60 } header_t;
61
62 /* print header values */
63
64 void print_header_values (header_t *header)
65 {
66 printf ("Header values\n");
67 if (header->allow) printf ("allow = '%s'\n", header->allow);
68 if (header->authorization) printf ("authorization = '%s'\n", header->authorization);
69 if (header->content_encoding) printf ("content_encoding = '%s'\n", header->content_encoding);
70 if (header->content_length) printf ("content_length = '%s'\n", header->content_length);
71 if (header->content_type) printf ("content_type = '%s'\n", header->content_type);
72 if (header->date) printf ("date = '%s'\n", header->date);
73 if (header->expires) printf ("expires = '%s'\n", header->expires);
74 if (header->from) printf ("from = '%s'\n", header->from);
75 if (header->if_modified_since) printf ("if_modified_since = '%s'\n", header->if_modified_since);
76 if (header->last_modified) printf ("last_modified = '%s'\n", header->last_modified);
77 if (header->location) printf ("location = '%s'\n", header->location);
78 if (header->pragma) printf ("pragma = '%s'\n", header->pragma);
79 if (header->referer) printf ("referer = '%s'\n", header->referer);
80 if (header->server) printf ("server = '%s'\n", header->server);
81 if (header->user_agent) printf ("user_agent = '%s'\n", header->user_agent);
82 if (header->www_authenticate) printf ("www_authenticate = '%s'\n", header->www_authenticate);
83 }
84
85 /* find sequence*/
86
87 char *find_sequence (char *data, int len, char *seq, char **pdata)
88 {
89
90 int size = strlen (seq);
91
92 int i;
93 for (i = 0; i < len - size + 1; i++) {
94 if (strncmp (data + i, seq, size) == 0) {
95 data[i] = 0;
96 if (pdata != NULL) {
97 *pdata = data + i + size;
98 }
99 return data;
100 }
101 }
102
103 return NULL;
104 }
105
106 /* response entity */
107
108 int add_line (char **buffer, char *str)
109 {
110 int len = strlen (*buffer) + strlen (str) + 1;
111 *buffer = realloc (*buffer, len);
112 strcat (*buffer, str);
113 return len;
114 }
115
116 int add_status_line (char **buffer, code_t code)
117 {
118 char tmp[BUFFER_SIZE] = {0};
119
120 /* Status */
121 sprintf (tmp, "%s %s\r\n", HTTP_VERSION, codes[code]);
122 add_line (buffer, tmp);
123
124 return strlen (*buffer);
125 }
126
127 int add_general_header (char **buffer)
128 {
129 char tmp[BUFFER_SIZE] = {0};
130
131 /* Date */
132 time_t ts = time (NULL);
133 sprintf (tmp, "Date: %s\r\n", ctime (&ts));
134 add_line (buffer, tmp);
135
136 /* Pragma */
137
138 return strlen (*buffer);
139 }
140
141 int add_response_header (char **buffer, char *uri)
142 {
143 char tmp[BUFFER_SIZE] = {0};
144
145 /* Location */
146 sprintf (tmp, "Location: %s\r\n", uri);
147 add_line (buffer, tmp);
148
149 /* Server */
150 sprintf (tmp, "Server: %s\r\n", SERVER_NAME);
151 add_line (buffer, tmp);
152
153 /* WWW-Authentificate */
154
155 return strlen (*buffer);
156 }
157
158 int add_entity (char **buffer, char *entity, int size, char *type, char *encoding)
159 {
160 char tmp[BUFFER_SIZE] = {0};
161 int len = strlen (*buffer);
162
163 /* Allow */
164 /* Expires */
165 /* Last-Modified */
166
167 if (entity != NULL) {
168
169 /* Content-Encoding */
170 if (encoding != NULL) {
171 sprintf (tmp, "Content-Encoding: %s\r\n", encoding);
172 add_line (buffer, tmp);
173 }
174
175 /* Content-Length */
176 sprintf (tmp, "Content-Length: %d\r\n", size);
177 add_line (buffer, tmp);
178
179 /* Content-Type */
180 sprintf (tmp, "Content-Type: %s\r\n", type);
181 add_line (buffer, tmp);
182
183 add_line (buffer, "\r\n");
184
185 /* Entity */
186 len = strlen (*buffer);
187 *buffer = realloc (*buffer, len + size);
188 memcpy (*buffer + len, entity, size);
189 len += size;
190 }
191
192 return len;
193 }
194
195 /* error 400 */
196
197 int error_400 (char **buffer)
198 {
199 add_status_line (buffer, c400);
200 add_general_header (buffer);
201 add_response_header (buffer, NULL);
202
203 char *response = "<html><head><title>Error 400</title></head><body><p>Bad Request</p></body></html>";
204 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
205 }
206
207 /* error 404 */
208
209 int error_404 (char **buffer, char *uri)
210 {
211 add_status_line (buffer, c404);
212 add_general_header (buffer);
213 add_response_header (buffer, uri);
214
215 char *response = "<html><head><title>Error 404</title></head><body><p>File not found</p></body></html>";
216 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
217 }
218
219 /* response html */
220
221 int response_html (char **buffer, char *location, char *response)
222 {
223 add_status_line (buffer, c200);
224 add_general_header (buffer);
225 add_response_header (buffer, location);
226
227 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
228 }
229
230 /* trim string */
231
232 char *trim (char *str)
233 {
234 if (str != NULL) {
235 while ((*str == ' ') || (*str == '\t')) {
236 str++;
237 }
238 }
239 return str;
240 }
241
242 /* main HTTP processing */
243
244 int processing (char *data, int len, char *root, char **pdata)
245 {
246 char *saved_data = data;
247 VERBOSE (DEBUG, PRINT ("Start processing\n"));
248
249 /* check method */
250 char *line = find_sequence (data, len, "\r\n", &data);
251 if (line == NULL) {
252 VERBOSE (WARNING, PRINT ("Unknown received data\n"));
253 if (pdata != NULL) {
254 *pdata = NULL;
255 }
256 return 0;
257 }
258 VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line));
259
260 char *method = strtok (line, " ");
261 char *uri = strtok (NULL, " ");
262 char *version = strtok (NULL, " ");
263 method_t type = not_supported_e;
264 if (strcmp (method, "GET") == 0) {
265 type = get_e;
266 } else if (strcmp ("HEAD", method) == 0) {
267 type = head_e;
268 } else if (strcmp ("POST", method) == 0) {
269 type = post_e;
270 } else {
271 VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
272 free (saved_data);
273 return error_400 (pdata);
274 }
275 VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version));
276
277 /* analyse uri */
278 char *filename = calloc (strlen (root) + strlen (uri) + 2, 1);
279 //sprintf (filename, "%s%s%s", root, ((root[strlen (root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
280 sprintf (filename, "%s/%s", root, uri);
281
282 /* check header */
283 header_t header = {0};
284 while (strcmp (line = find_sequence (data, len, "\r\n", &data), "") != 0) {
285 VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line));
286 char *field = strtok (line, ":");
287 char *value = trim (strtok (NULL, "\r"));
288 VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value));
289 if (*line == 0) {
290 break;
291 }
292
293 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
294 if (strcmp (field, "Allow") == 0) {
295 header.allow = value;
296 } else if (strcmp (field, "Authorization") == 0) {
297 header.authorization = value;
298 } else if (strcmp (field, "Content-Encoding") == 0) {
299 header.content_encoding = value;
300 } else if (strcmp (field, "Content-Length") == 0) {
301 header.content_length = value;
302 } else if (strcmp (field, "Content-Type") == 0) {
303 header.content_type = value;
304 } else if (strcmp (field, "Date") == 0) {
305 header.date = value;
306 } else if (strcmp (field, "Expires") == 0) {
307 header.expires = value;
308 } else if (strcmp (field, "From") == 0) {
309 header.from = value;
310 } else if (strcmp (field, "If-Modified-Since") == 0) {
311 header.if_modified_since = value;
312 } else if (strcmp (field, "Last-Modified") == 0) {
313 header.last_modified = value;
314 } else if (strcmp (field, "Location") == 0) {
315 header.location = value;
316 } else if (strcmp (field, "Pragma") == 0) {
317 header.pragma = value;
318 } else if (strcmp (field, "Referer") == 0) {
319 header.referer = value;
320 } else if (strcmp (field, "Server") == 0) {
321 header.server = value;
322 } else if (strcmp (field, "User-Agent") == 0) {
323 header.user_agent = value;
324 } else if (strcmp (field, "WWW-Authenticate") == 0) {
325 header.www_authenticate = value;
326 } else {
327 VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field));
328 }
329 }
330 VERBOSE (DEBUG, print_header_values (&header));
331
332 /* processing data */
333 if (data) {
334 VERBOSE (DEBUG, PRINT ("Processing data: %s\n", data));
335 }
336
337 /* response */
338 char *buffer = NULL;
339 switch (type) {
340 case get_e:
341 len = readfile (&buffer, filename);
342 if (len == 0) {
343 len = error_404 (pdata, "http://localhost/");
344 } else {
345 len = response_html (pdata, "http://localhost/", buffer);
346 free (buffer);
347 }
348 break;
349 case head_e:
350 break;
351 case post_e:
352 break;
353 case not_supported_e:
354 break;
355 }
356
357 /* cleaning */
358 free (saved_data);
359 free (filename);
360
361 return len;
362 }
363
364 /* vim: set ts=4 sw=4 et: */