minimal mime management
[webserver.git] / http.c
1 #include <malloc.h>
2 #include <string.h>
3 #include <time.h>
4
5 #include "debug.h"
6 #include "file.h"
7
8 #include "http.h"
9
10 #define BUFFER_SIZE 128
11
12 #define HTTP_VERSION "HTTP/1.0"
13 #define SERVER_NAME "Webserver/0.0.1"
14
15 char *codes[15] = {
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
32 typedef 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
39 typedef enum {
40 not_supported_e = 0, get_e, head_e, post_e
41 } method_t;
42
43 typedef 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
62 typedef struct {
63 char *ext;
64 char *type;
65 char *charset;
66 } mime_t;
67
68 #define NB_MIMES 8
69
70 mime_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
83 mime_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
107 /* print header values */
108
109 void print_header_values (header_t *header)
110 {
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);
128 }
129
130 /* find sequence*/
131
132 char *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
153 int add_line (char **buffer, char *str)
154 {
155 VERBOSE (DEBUG, PRINT ("add line: %s\n", str));
156 int len = ((*buffer) ? strlen (*buffer) : 0) + strlen (str) + 3;
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 }
163 strcat (*buffer, str);
164 strcat (*buffer, "\r\n");
165 return len;
166 }
167
168 int add_status_line (char **buffer, code_t code)
169 {
170 char tmp[BUFFER_SIZE] = {0};
171
172 /* Status */
173 sprintf (tmp, "%s %s", HTTP_VERSION, codes[code]);
174 add_line (buffer, tmp);
175
176 return strlen (*buffer);
177 }
178
179 int add_general_header (char **buffer)
180 {
181 char tmp[BUFFER_SIZE] = {0};
182
183 /* Date */
184 time_t ts = time (NULL);
185 sprintf (tmp, "Date: %s", ctime (&ts));
186 tmp[strlen (tmp) - 1] = 0; // remove last \n
187 add_line (buffer, tmp);
188
189 /* Pragma */
190
191 return strlen (*buffer);
192 }
193
194 int add_response_header (char **buffer, char *uri)
195 {
196 char tmp[BUFFER_SIZE] = {0};
197
198 /* Location */
199 sprintf (tmp, "Location: %s", uri);
200 add_line (buffer, tmp);
201
202 /* Server */
203 sprintf (tmp, "Server: %s", SERVER_NAME);
204 add_line (buffer, tmp);
205
206 /* WWW-Authentificate */
207
208 return strlen (*buffer);
209 }
210
211 int add_entity (char **buffer, char *entity, int size, char *type, char *encoding)
212 {
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) {
224 sprintf (tmp, "Content-Encoding: %sn", encoding);
225 add_line (buffer, tmp);
226 }
227
228 /* Content-Length */
229 sprintf (tmp, "Content-Length: %d", size);
230 add_line (buffer, tmp);
231
232 /* Content-Type */
233 sprintf (tmp, "Content-Type: %s", type);
234 add_line (buffer, tmp);
235
236 add_line (buffer, "");
237
238 /* Entity */
239 len = strlen (*buffer);
240 *buffer = realloc (*buffer, len + size);
241 memcpy (*buffer + len, entity, size);
242 len += size;
243 }
244
245 return len;
246 }
247
248 /* error 400 */
249
250 int error_400 (char **buffer)
251 {
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>";
257 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
258 }
259
260 /* error 404 */
261
262 int 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>";
269 return add_entity (buffer, response, strlen (response), "text/html", "iso-8859-1");
270 }
271
272 /* generic response */
273
274 int generic_response (char **buffer, char *location, char *response, int size)
275 {
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);
283 mime_t *mime = find_mime_type (location);
284
285 VERBOSE (DEBUG, PRINT ("add_entity %d\n", len));
286 return add_entity (buffer, response, size, mime->type, mime->charset);
287 }
288
289 /* trim string */
290
291 char *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
303 int processing (char *data, int len, conf_t *conf, char **pdata)
304 {
305 char location[BUFFER_SIZE] = {0};
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));
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
335 /* analyse uri */
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);
339
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
390 /* response */
391 char *buffer = NULL;
392 switch (type) {
393 case get_e:
394 VERBOSE (DEBUG, PRINT ("Read file %s\n", filename));
395 len = readfile (&buffer, filename);
396 if (len == 0) {
397 sprintf (location, "http://%s/", conf->servername);
398 len = error_404 (pdata, location);
399 } else {
400 sprintf (location, "http://%s%s", conf->servername, filename);
401 len = generic_response (pdata, location, buffer, len);
402 free (buffer);
403 }
404 break;
405 case head_e:
406 break;
407 case post_e:
408 VERBOSE (DEBUG, PRINT ("Write file %s\n", filename));
409 break;
410 case not_supported_e:
411 break;
412 }
413
414 /* cleaning */
415 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
416 if (filename) {
417 VERBOSE (DEBUG, PRINT ("Cleaning filename\n"));
418 free (filename);
419 }
420
421 return len;
422 }
423
424 /* vim: set ts=4 sw=4 et: */