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