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