best makefile
[webserver.git] / http.c
1 #include <malloc.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <time.h>
5 #include <unistd.h>
6
7 #include "debug.h"
8 #include "file.h"
9
10 #include "http.h"
11
12 #define BUFFER_SIZE 128
13
14 #define HTTP_VERSION "HTTP/1.0"
15 #define SERVER_NAME "Webserver/0.0.1"
16
17 char *codes[15] = {
18 "200 OK",
19 "201 Created",
20 "202 Accepted",
21 "204 No Content",
22 "301 Moved Permanently",
23 "302 Moved Temporarily",
24 "304 Not Modified",
25 "400 Bad Request",
26 "401 Unauthorized",
27 "403 Forbidden",
28 "404 Not Found",
29 "500 Internal Server Error",
30 "501 Not Implemented",
31 "502 Bad Gateway",
32 "503 Service Unavailable"};
33
34 typedef enum {
35 c200 = 0, c201, c202, c204,
36 c301, c302, c304, c400,
37 c401, c403, c404, c500,
38 c501, c502, c503
39 } code_t;
40
41 typedef enum {
42 not_supported_e = 0, get_e, head_e, post_e
43 } method_t;
44
45 typedef enum {
46 encoding_plain_e = 0, encoding_gzip_e, encoding_compress_e
47 } encoding_t;
48
49 typedef struct {
50 char *allow;
51 char *authorization;
52 encoding_t content_encoding;
53 char *content_length;
54 char *content_type;
55 char *date;
56 char *expires;
57 char *from;
58 char *if_modified_since;
59 char *last_modified;
60 char *location;
61 char *pragma;
62 char *referer;
63 char *server;
64 char *user_agent;
65 char *www_authenticate;
66 } header_t;
67
68 typedef struct {
69 char *ext;
70 char *type;
71 char *charset;
72 } mime_t;
73
74 #define NB_MIMES 8
75
76 mime_t mimes[NB_MIMES] = {
77 {"js", "application/javascript", "iso-8859-1"},
78 {"css", "text/css", "iso-8859-1"},
79 {"htm", "text/html", "iso-8859-1"},
80 {"html", "text/html", "iso-8859-1"},
81 {"png", "image/png", NULL},
82 {"jpeg", "image/jpeg", NULL},
83 {"jpg", "image/jpeg", NULL},
84 {"txt", "text/plain", "iso-8859-1"}
85 };
86
87 /* find mime type */
88
89 mime_t *find_mime_type (char *filename)
90 {
91 /* find extention */
92 char *ext = filename + strlen (filename);
93 while (--ext > filename) {
94 if (ext[-1] == '.') {
95 break;
96 }
97 }
98 if (ext == filename) {
99 return NULL;
100 }
101
102 /* find mime */
103 int i;
104 for (i = 0; i < NB_MIMES; i++) {
105 if (strcmp (ext, (mimes + i)->ext) == 0) {
106 return mimes + i;
107 }
108 }
109
110 return NULL;
111 }
112
113 /* print header values */
114
115 void print_header_values (header_t *header)
116 {
117 printf ("Header values\n");
118 if (header->allow) printf ("allow = '%s'\n", header->allow);
119 if (header->authorization) printf ("authorization = '%s'\n", header->authorization);
120 if (header->content_encoding) {
121 printf ("content_encoding = ");
122 switch (header->content_encoding) {
123 case encoding_plain_e: printf ("plain\n"); break;
124 case encoding_gzip_e: printf ("gzip\n"); break;
125 case encoding_compress_e: printf ("compress\n"); break;
126 }
127 }
128 if (header->content_length) printf ("content_length = '%s'\n", header->content_length);
129 if (header->content_type) printf ("content_type = '%s'\n", header->content_type);
130 if (header->date) printf ("date = '%s'\n", header->date);
131 if (header->expires) printf ("expires = '%s'\n", header->expires);
132 if (header->from) printf ("from = '%s'\n", header->from);
133 if (header->if_modified_since) printf ("if_modified_since = '%s'\n", header->if_modified_since);
134 if (header->last_modified) printf ("last_modified = '%s'\n", header->last_modified);
135 if (header->location) printf ("location = '%s'\n", header->location);
136 if (header->pragma) printf ("pragma = '%s'\n", header->pragma);
137 if (header->referer) printf ("referer = '%s'\n", header->referer);
138 if (header->server) printf ("server = '%s'\n", header->server);
139 if (header->user_agent) printf ("user_agent = '%s'\n", header->user_agent);
140 if (header->www_authenticate) printf ("www_authenticate = '%s'\n", header->www_authenticate);
141 }
142
143 /* find sequence*/
144
145 char *find_sequence (char *data, int len, char *seq, char **pdata)
146 {
147
148 int size = strlen (seq);
149
150 int i;
151 for (i = 0; i < len - size + 1; i++) {
152 if (strncmp (data + i, seq, size) == 0) {
153 data[i] = 0;
154 if (pdata != NULL) {
155 *pdata = data + i + size;
156 }
157 return data;
158 }
159 }
160
161 return NULL;
162 }
163
164 /* response entity */
165
166 int add_line (char **buffer, char *str)
167 {
168 VERBOSE (DEBUG, PRINT ("add line: %s\n", str));
169 int len = ((*buffer) ? strlen (*buffer) : 0) + strlen (str) + 3;
170 VERBOSE (DEBUG, PRINT ("len: %d\n", len));
171 if (*buffer) {
172 *buffer = (char *)realloc (*buffer, len);
173 } else {
174 *buffer = (char *)calloc (len, 1);
175 }
176 strcat (*buffer, str);
177 strcat (*buffer, "\r\n");
178 return len;
179 }
180
181 int add_status_line (char **buffer, code_t code)
182 {
183 char tmp[BUFFER_SIZE] = {0};
184
185 /* Status */
186 sprintf (tmp, "%s %s", HTTP_VERSION, codes[code]);
187 add_line (buffer, tmp);
188
189 return strlen (*buffer);
190 }
191
192 int add_general_header (char **buffer)
193 {
194 char tmp[BUFFER_SIZE] = {0};
195
196 /* Date */
197 time_t ts = time (NULL);
198 sprintf (tmp, "Date: %s", ctime (&ts));
199 tmp[strlen (tmp) - 1] = 0; // remove last \n
200 add_line (buffer, tmp);
201
202 /* Pragma */
203
204 return strlen (*buffer);
205 }
206
207 int add_response_header (char **buffer, char *uri)
208 {
209 char tmp[BUFFER_SIZE] = {0};
210
211 /* Location */
212 sprintf (tmp, "Location: %s", uri);
213 add_line (buffer, tmp);
214
215 /* Server */
216 sprintf (tmp, "Server: %s", SERVER_NAME);
217 add_line (buffer, tmp);
218
219 /* WWW-Authentificate */
220
221 return strlen (*buffer);
222 }
223
224 int add_entity (char **buffer, char *entity, int size, char *type, char *encoding)
225 {
226 char tmp[BUFFER_SIZE] = {0};
227 int len = strlen (*buffer);
228
229 /* Allow */
230 /* Expires */
231 /* Last-Modified */
232
233 if (entity != NULL) {
234
235 /* Content-Encoding */
236 if (encoding != NULL) {
237 sprintf (tmp, "Content-Encoding: %s", encoding);
238 add_line (buffer, tmp);
239 }
240
241 /* Content-Length */
242 sprintf (tmp, "Content-Length: %d", size);
243 add_line (buffer, tmp);
244
245 /* Content-Type */
246 sprintf (tmp, "Content-Type: %s", type);
247 add_line (buffer, tmp);
248
249 add_line (buffer, "");
250
251 /* Entity */
252 len = strlen (*buffer);
253 *buffer = realloc (*buffer, len + size);
254 memcpy (*buffer + len, entity, size);
255 len += size;
256 }
257
258 return len;
259 }
260
261 /* error 400 */
262
263 int error_400 (char **buffer)
264 {
265 add_status_line (buffer, c400);
266 add_general_header (buffer);
267 add_response_header (buffer, NULL);
268
269 char *response = "<html><head><title>Error 400</title></head><body><p>Bad Request</p></body></html>";
270 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
271 }
272
273 /* error 404 */
274
275 int error_404 (char **buffer, char *uri)
276 {
277 add_status_line (buffer, c404);
278 add_general_header (buffer);
279 add_response_header (buffer, uri);
280
281 char *response = "<html><head><title>Error 404</title></head><body><p>File not found</p></body></html>";
282 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
283 }
284
285 /* generic response */
286
287 int generic_response (char **buffer, char *location, char *response, int size)
288 {
289 int len = 0;
290 VERBOSE (DEBUG, PRINT ("add_status_line %d\n", len));
291 len = add_status_line (buffer, c200);
292 VERBOSE (DEBUG, PRINT ("add_general_header %d\n", len));
293 len = add_general_header (buffer);
294 VERBOSE (DEBUG, PRINT ("add_response_header %d\n", len));
295 len = add_response_header (buffer, location);
296 mime_t *mime = find_mime_type (location);
297
298 VERBOSE (DEBUG, PRINT ("add_entity %d\n", len));
299 return add_entity (buffer, response, size, mime->type, mime->charset);
300 }
301
302 /* trim string */
303
304 char *trim (char *str)
305 {
306 if (str != NULL) {
307 while ((*str == ' ') || (*str == '\t')) {
308 str++;
309 }
310 }
311 return str;
312 }
313
314 /* creqte command */
315
316 char *createcommand (char *format, char *name)
317 {
318 char *command = (char *) calloc (strlen (format) + strlen (name) + 1, 1);
319 sprintf (command, format, name);
320 return command;
321 }
322
323 /* main HTTP processing */
324
325 int processing (char *data, int len, conf_t *conf, char **pdata)
326 {
327 char *saved_data = data;
328 char location[BUFFER_SIZE] = {0};
329 VERBOSE (DEBUG, PRINT ("Start processing\n"));
330
331 /* check method */
332 char *line = find_sequence (data, len + data - saved_data, "\r\n", &data);
333 if (line == NULL) {
334 VERBOSE (WARNING, PRINT ("Unknown received data\n"));
335 if (pdata != NULL) {
336 *pdata = NULL;
337 }
338 return 0;
339 }
340 VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line));
341
342 char *method = strtok (line, " ");
343 char *uri = strtok (NULL, " ");
344 char *version = strtok (NULL, " ");
345 method_t type = not_supported_e;
346 if (strcmp (method, "GET") == 0) {
347 type = get_e;
348 } else if (strcmp ("HEAD", method) == 0) {
349 type = head_e;
350 } else if (strcmp ("POST", method) == 0) {
351 type = post_e;
352 } else {
353 VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
354 return error_400 (pdata);
355 }
356 VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version));
357
358 /* analyse uri */
359 char *filename = strtok (uri, "&");
360 char *variables = strtok (NULL, "\n");
361 char *path = (char *) calloc (strlen (conf->root) + strlen (filename) + 2, 1);
362 //sprintf (filename, "%s%s%s", conf->root, ((conf->root[strlen (conf->root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
363 sprintf (path, "%s/%s", conf->root, filename);
364
365 /* check header */
366 header_t header = {0};
367 while (strcmp (line = find_sequence (data, len + data - saved_data, "\r\n", &data), "") != 0) {
368 VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line));
369 char *field = strtok (line, ":");
370 char *value = trim (strtok (NULL, "\r"));
371 VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value));
372 if (*line == 0) {
373 break;
374 }
375
376 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
377 if (strcmp (field, "Allow") == 0) { header.allow = value; }
378 else if (strcmp (field, "Authorization") == 0) { header.authorization = value; }
379 else if (strcmp (field, "Content-Encoding") == 0) {
380 if (strcmp (value, "x-gzip") == 0) {
381 header.content_encoding = encoding_gzip_e;
382 } else if (strcmp (value, "x-compress") == 0) {
383 header.content_encoding = encoding_compress_e;
384 } else {
385 VERBOSE (WARNING, PRINT ("Unknown content encoding: %s\n", value));
386 }
387 } else if (strcmp (field, "Content-Length") == 0) { header.content_length = value; }
388 else if (strcmp (field, "Content-Type") == 0) { header.content_type = value; }
389 else if (strcmp (field, "Date") == 0) { header.date = value; }
390 else if (strcmp (field, "Expires") == 0) { header.expires = value; }
391 else if (strcmp (field, "From") == 0) { header.from = value; }
392 else if (strcmp (field, "If-Modified-Since") == 0) { header.if_modified_since = value; }
393 else if (strcmp (field, "Last-Modified") == 0) { header.last_modified = value; }
394 else if (strcmp (field, "Location") == 0) { header.location = value; }
395 else if (strcmp (field, "Pragma") == 0) { header.pragma = value; }
396 else if (strcmp (field, "Referer") == 0) { header.referer = value; }
397 else if (strcmp (field, "Server") == 0) { header.server = value; }
398 else if (strcmp (field, "User-Agent") == 0) { header.user_agent = value; }
399 else if (strcmp (field, "WWW-Authenticate") == 0) { header.www_authenticate = value; }
400 else { VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field)); }
401 }
402 VERBOSE (DEBUG, print_header_values (&header));
403
404 /* body */
405 char *body = data;
406 char *newbody = NULL;
407 len -= saved_data - data;
408 if (len != (header.content_length ? atoi (header.content_length) : 0)) {
409 VERBOSE (WARNING, PRINT ("Incoherent size (%d <> %s)\n", len, header.content_length));
410 }
411 if (len > 0) {
412 int i;
413 char *fcomp= NULL;
414 char *fdecomp = NULL;
415 char *command = NULL;
416
417 switch (header.content_encoding) {
418 case encoding_plain_e:
419 break;
420
421 case encoding_gzip_e:
422 fcomp = tempname (conf->temp, ".gz");
423 writefile (fcomp, body, len);
424 command = createcommand ("gunzip %s", fcomp);
425 system (command);
426 fdecomp = strdup (fcomp);
427 for (i = strlen (fdecomp) - 1; i > 0; i--) {
428 if (fdecomp[i] == '.') {
429 fdecomp[i] = 0;
430 break;
431 }
432 }
433 len = readfile (&newbody, fdecomp);
434 break;
435
436 case encoding_compress_e:
437 fcomp = tempname (conf->temp, ".Z");
438 writefile (fcomp, body, len);
439 command = createcommand ("compress %s", fcomp);
440 system (command);
441 fdecomp = strdup (fcomp);
442 for (i = strlen (fdecomp) - 1; i > 0; i--) {
443 if (fdecomp[i] == '.') {
444 fdecomp[i] = 0;
445 break;
446 }
447 }
448 len = readfile (&newbody, fdecomp);
449 break;
450 }
451
452 if (fcomp) {
453 unlink (fcomp);
454 free (fcomp);
455 }
456 if (fdecomp) {
457 unlink (fdecomp);
458 free (fdecomp);
459 }
460 if (command) {
461 free (command);
462 }
463 }
464
465 /* response */
466 char *buffer = NULL;
467 FILE *fid = NULL;
468 switch (type) {
469 case get_e:
470 case post_e: /* fall through */
471 VERBOSE (DEBUG, PRINT ("Read file %s\n", path));
472 len = readfile (&buffer, path);
473 if (len < 0) {
474 sprintf (location, "http://%s/", conf->servername);
475 len = error_404 (pdata, location);
476 } else {
477 sprintf (location, "http://%s%s", conf->servername, path);
478 len = generic_response (pdata, location, buffer, len);
479 free (buffer);
480 }
481 break;
482 case head_e:
483 VERBOSE (DEBUG, PRINT ("Test file %s\n", path));
484 fid = fopen (path, "rb");
485 if (fid == NULL) {
486 sprintf (location, "http://%s/", conf->servername);
487 len = error_404 (pdata, location);
488 } else {
489 fclose (fid);
490 sprintf (location, "http://%s%s", conf->servername, path);
491 len = generic_response (pdata, location, NULL, 0);
492 }
493 break;
494 case not_supported_e:
495 break;
496 }
497
498 /* cleaning */
499 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
500 if (path) {
501 free (path);
502 }
503 if (newbody) {
504 free (newbody);
505 }
506
507 return len;
508 }
509
510 /* vim: set ts=4 sw=4 et: */