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