minimal mime management
[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) {
2bec4aa7 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 */
309 char *line = find_sequence (data, len, "\r\n", &data);
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 */
2bec4aa7
LM
336 char *filename = (char *)calloc (strlen (conf->root) + strlen (uri) + 2, 1);
337 //sprintf (filename, "%s%s%s", conf->root, ((conf->root[strlen (conf->root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
338 sprintf (filename, "%s/%s", conf->root, uri);
4baf6839 339
bb0468a5
LM
340 /* check header */
341 header_t header = {0};
342 while (strcmp (line = find_sequence (data, len, "\r\n", &data), "") != 0) {
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"));
352 if (strcmp (field, "Allow") == 0) {
353 header.allow = value;
354 } else if (strcmp (field, "Authorization") == 0) {
355 header.authorization = value;
356 } else if (strcmp (field, "Content-Encoding") == 0) {
357 header.content_encoding = value;
358 } else if (strcmp (field, "Content-Length") == 0) {
359 header.content_length = value;
360 } else if (strcmp (field, "Content-Type") == 0) {
361 header.content_type = value;
362 } else if (strcmp (field, "Date") == 0) {
363 header.date = value;
364 } else if (strcmp (field, "Expires") == 0) {
365 header.expires = value;
366 } else if (strcmp (field, "From") == 0) {
367 header.from = value;
368 } else if (strcmp (field, "If-Modified-Since") == 0) {
369 header.if_modified_since = value;
370 } else if (strcmp (field, "Last-Modified") == 0) {
371 header.last_modified = value;
372 } else if (strcmp (field, "Location") == 0) {
373 header.location = value;
374 } else if (strcmp (field, "Pragma") == 0) {
375 header.pragma = value;
376 } else if (strcmp (field, "Referer") == 0) {
377 header.referer = value;
378 } else if (strcmp (field, "Server") == 0) {
379 header.server = value;
380 } else if (strcmp (field, "User-Agent") == 0) {
381 header.user_agent = value;
382 } else if (strcmp (field, "WWW-Authenticate") == 0) {
383 header.www_authenticate = value;
384 } else {
385 VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field));
386 }
387 }
388 VERBOSE (DEBUG, print_header_values (&header));
389
bb0468a5 390 /* response */
4baf6839 391 char *buffer = NULL;
8a1d9e4a
LM
392 switch (type) {
393 case get_e:
6add28e9 394 VERBOSE (DEBUG, PRINT ("Read file %s\n", filename));
cae06547 395 len = readfile (&buffer, filename);
4baf6839 396 if (len == 0) {
2bec4aa7
LM
397 sprintf (location, "http://%s/", conf->servername);
398 len = error_404 (pdata, location);
4baf6839 399 } else {
2bec4aa7
LM
400 sprintf (location, "http://%s%s", conf->servername, filename);
401 len = generic_response (pdata, location, buffer, len);
4baf6839
LM
402 free (buffer);
403 }
8a1d9e4a
LM
404 break;
405 case head_e:
406 break;
407 case post_e:
6add28e9 408 VERBOSE (DEBUG, PRINT ("Write file %s\n", filename));
8a1d9e4a
LM
409 break;
410 case not_supported_e:
411 break;
412 }
413
4baf6839 414 /* cleaning */
6add28e9
LM
415 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
416 if (filename) {
417 VERBOSE (DEBUG, PRINT ("Cleaning filename\n"));
418 free (filename);
419 }
4baf6839 420
8a1d9e4a 421 return len;
bb0468a5
LM
422}
423
d0b0d52b 424/* vim: set ts=4 sw=4 et: */