best makefile
[webserver.git] / http.c
CommitLineData
d0b0d52b 1#include <malloc.h>
0e6fd2e9 2#include <stdlib.h>
d0b0d52b 3#include <string.h>
8a1d9e4a 4#include <time.h>
0a7c57da 5#include <unistd.h>
d0b0d52b
ML
6
7#include "debug.h"
4baf6839 8#include "file.h"
d0b0d52b
ML
9
10#include "http.h"
11
8a1d9e4a
LM
12#define BUFFER_SIZE 128
13
bb0468a5
LM
14#define HTTP_VERSION "HTTP/1.0"
15#define SERVER_NAME "Webserver/0.0.1"
16
8a1d9e4a 17char *codes[15] = {
bb0468a5
LM
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
34typedef 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
41typedef enum {
42 not_supported_e = 0, get_e, head_e, post_e
43} method_t;
44
7fc5f3c6
LM
45typedef enum {
46 encoding_plain_e = 0, encoding_gzip_e, encoding_compress_e
47} encoding_t;
48
bb0468a5
LM
49typedef struct {
50 char *allow;
51 char *authorization;
7fc5f3c6 52 encoding_t content_encoding;
bb0468a5
LM
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
2bec4aa7
LM
68typedef struct {
69 char *ext;
70 char *type;
71 char *charset;
72} mime_t;
73
74#define NB_MIMES 8
75
76mime_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
89mime_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
bb0468a5
LM
113/* print header values */
114
115void print_header_values (header_t *header)
116{
cae06547
LM
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);
7fc5f3c6
LM
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 }
cae06547
LM
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);
bb0468a5
LM
141}
142
143/* find sequence*/
144
145char *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
8a1d9e4a 166int add_line (char **buffer, char *str)
bb0468a5 167{
2bec4aa7
LM
168 VERBOSE (DEBUG, PRINT ("add line: %s\n", str));
169 int len = ((*buffer) ? strlen (*buffer) : 0) + strlen (str) + 3;
50c7ef81
LM
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 }
8a1d9e4a 176 strcat (*buffer, str);
2bec4aa7 177 strcat (*buffer, "\r\n");
d0b0d52b
ML
178 return len;
179}
180
8a1d9e4a 181int add_status_line (char **buffer, code_t code)
bb0468a5 182{
8a1d9e4a
LM
183 char tmp[BUFFER_SIZE] = {0};
184
185 /* Status */
2bec4aa7 186 sprintf (tmp, "%s %s", HTTP_VERSION, codes[code]);
8a1d9e4a
LM
187 add_line (buffer, tmp);
188
189 return strlen (*buffer);
bb0468a5
LM
190}
191
8a1d9e4a 192int add_general_header (char **buffer)
bb0468a5 193{
8a1d9e4a
LM
194 char tmp[BUFFER_SIZE] = {0};
195
196 /* Date */
197 time_t ts = time (NULL);
2bec4aa7
LM
198 sprintf (tmp, "Date: %s", ctime (&ts));
199 tmp[strlen (tmp) - 1] = 0; // remove last \n
8a1d9e4a
LM
200 add_line (buffer, tmp);
201
202 /* Pragma */
203
204 return strlen (*buffer);
205}
206
207int add_response_header (char **buffer, char *uri)
208{
209 char tmp[BUFFER_SIZE] = {0};
210
211 /* Location */
2bec4aa7 212 sprintf (tmp, "Location: %s", uri);
8a1d9e4a
LM
213 add_line (buffer, tmp);
214
215 /* Server */
2bec4aa7 216 sprintf (tmp, "Server: %s", SERVER_NAME);
8a1d9e4a
LM
217 add_line (buffer, tmp);
218
219 /* WWW-Authentificate */
220
221 return strlen (*buffer);
bb0468a5
LM
222}
223
cae06547 224int add_entity (char **buffer, char *entity, int size, char *type, char *encoding)
bb0468a5 225{
8a1d9e4a
LM
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) {
0e6fd2e9 237 sprintf (tmp, "Content-Encoding: %s", encoding);
8a1d9e4a
LM
238 add_line (buffer, tmp);
239 }
240
241 /* Content-Length */
2bec4aa7 242 sprintf (tmp, "Content-Length: %d", size);
8a1d9e4a
LM
243 add_line (buffer, tmp);
244
245 /* Content-Type */
2bec4aa7 246 sprintf (tmp, "Content-Type: %s", type);
8a1d9e4a
LM
247 add_line (buffer, tmp);
248
2bec4aa7 249 add_line (buffer, "");
8a1d9e4a
LM
250
251 /* Entity */
252 len = strlen (*buffer);
253 *buffer = realloc (*buffer, len + size);
254 memcpy (*buffer + len, entity, size);
255 len += size;
256 }
257
bb0468a5
LM
258 return len;
259}
260
261/* error 400 */
262
263int error_400 (char **buffer)
264{
8a1d9e4a
LM
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>";
cae06547 270 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
8a1d9e4a
LM
271}
272
4baf6839
LM
273/* error 404 */
274
275int 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>";
cae06547 282 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
4baf6839
LM
283}
284
2bec4aa7 285/* generic response */
8a1d9e4a 286
2bec4aa7 287int generic_response (char **buffer, char *location, char *response, int size)
8a1d9e4a 288{
50c7ef81
LM
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);
2bec4aa7 296 mime_t *mime = find_mime_type (location);
50c7ef81
LM
297
298 VERBOSE (DEBUG, PRINT ("add_entity %d\n", len));
2bec4aa7 299 return add_entity (buffer, response, size, mime->type, mime->charset);
bb0468a5
LM
300}
301
302/* trim string */
303
304char *trim (char *str)
305{
306 if (str != NULL) {
307 while ((*str == ' ') || (*str == '\t')) {
308 str++;
309 }
310 }
311 return str;
312}
313
6ae861c7
LM
314/* creqte command */
315
316char *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
bb0468a5
LM
323/* main HTTP processing */
324
2bec4aa7 325int processing (char *data, int len, conf_t *conf, char **pdata)
bb0468a5 326{
0e6fd2e9 327 char *saved_data = data;
2bec4aa7 328 char location[BUFFER_SIZE] = {0};
bb0468a5
LM
329 VERBOSE (DEBUG, PRINT ("Start processing\n"));
330
331 /* check method */
0e6fd2e9 332 char *line = find_sequence (data, len + data - saved_data, "\r\n", &data);
bb0468a5
LM
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));
bb0468a5
LM
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
4baf6839 358 /* analyse uri */
0e6fd2e9
LM
359 char *filename = strtok (uri, "&");
360 char *variables = strtok (NULL, "\n");
361 char *path = (char *) calloc (strlen (conf->root) + strlen (filename) + 2, 1);
2bec4aa7 362 //sprintf (filename, "%s%s%s", conf->root, ((conf->root[strlen (conf->root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
0e6fd2e9 363 sprintf (path, "%s/%s", conf->root, filename);
4baf6839 364
bb0468a5
LM
365 /* check header */
366 header_t header = {0};
0e6fd2e9 367 while (strcmp (line = find_sequence (data, len + data - saved_data, "\r\n", &data), "") != 0) {
bb0468a5
LM
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 }
6ae861c7 375
bb0468a5 376 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
62584064
LM
377 if (strcmp (field, "Allow") == 0) { header.allow = value; }
378 else if (strcmp (field, "Authorization") == 0) { header.authorization = value; }
7fc5f3c6
LM
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; }
62584064
LM
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)); }
bb0468a5
LM
401 }
402 VERBOSE (DEBUG, print_header_values (&header));
403
0e6fd2e9
LM
404 /* body */
405 char *body = data;
6ae861c7 406 char *newbody = NULL;
0e6fd2e9
LM
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 }
7fc5f3c6 411 if (len > 0) {
6ae861c7
LM
412 int i;
413 char *fcomp= NULL;
414 char *fdecomp = NULL;
415 char *command = NULL;
416
7fc5f3c6 417 switch (header.content_encoding) {
6ae861c7
LM
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);
7fc5f3c6
LM
462 }
463 }
0e6fd2e9 464
bb0468a5 465 /* response */
4baf6839 466 char *buffer = NULL;
62584064 467 FILE *fid = NULL;
8a1d9e4a
LM
468 switch (type) {
469 case get_e:
0e6fd2e9
LM
470 case post_e: /* fall through */
471 VERBOSE (DEBUG, PRINT ("Read file %s\n", path));
472 len = readfile (&buffer, path);
d256f755 473 if (len < 0) {
2bec4aa7
LM
474 sprintf (location, "http://%s/", conf->servername);
475 len = error_404 (pdata, location);
4baf6839 476 } else {
0e6fd2e9 477 sprintf (location, "http://%s%s", conf->servername, path);
2bec4aa7 478 len = generic_response (pdata, location, buffer, len);
4baf6839
LM
479 free (buffer);
480 }
8a1d9e4a
LM
481 break;
482 case head_e:
0e6fd2e9
LM
483 VERBOSE (DEBUG, PRINT ("Test file %s\n", path));
484 fid = fopen (path, "rb");
62584064
LM
485 if (fid == NULL) {
486 sprintf (location, "http://%s/", conf->servername);
487 len = error_404 (pdata, location);
488 } else {
489 fclose (fid);
0e6fd2e9 490 sprintf (location, "http://%s%s", conf->servername, path);
62584064
LM
491 len = generic_response (pdata, location, NULL, 0);
492 }
8a1d9e4a 493 break;
8a1d9e4a
LM
494 case not_supported_e:
495 break;
496 }
497
4baf6839 498 /* cleaning */
6add28e9 499 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
0e6fd2e9 500 if (path) {
0e6fd2e9 501 free (path);
6add28e9 502 }
6ae861c7
LM
503 if (newbody) {
504 free (newbody);
505 }
4baf6839 506
8a1d9e4a 507 return len;
bb0468a5
LM
508}
509
d0b0d52b 510/* vim: set ts=4 sw=4 et: */