quick commit
[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 /* print header values */
63
64 void print_header_values (header_t *header)
65 {
66 printf ("allow = '%s'\n", header->allow);
67 printf ("authorization = '%s'\n", header->authorization);
68 printf ("content_encoding = '%s'\n", header->content_encoding);
69 printf ("content_length = '%s'\n", header->content_length);
70 printf ("content_type = '%s'\n", header->content_type);
71 printf ("date = '%s'\n", header->date);
72 printf ("expires = '%s'\n", header->expires);
73 printf ("from = '%s'\n", header->from);
74 printf ("if_modified_since = '%s'\n", header->if_modified_since);
75 printf ("last_modified = '%s'\n", header->last_modified);
76 printf ("location = '%s'\n", header->location);
77 printf ("pragma = '%s'\n", header->pragma);
78 printf ("referer = '%s'\n", header->referer);
79 printf ("server = '%s'\n", header->server);
80 printf ("user_agent = '%s'\n", header->user_agent);
81 printf ("www_authenticate = '%s'\n", header->www_authenticate);
82 }
83
84 /* find sequence*/
85
86 char *find_sequence (char *data, int len, char *seq, char **pdata)
87 {
88
89 int size = strlen (seq);
90
91 int i;
92 for (i = 0; i < len - size + 1; i++) {
93 if (strncmp (data + i, seq, size) == 0) {
94 data[i] = 0;
95 if (pdata != NULL) {
96 *pdata = data + i + size;
97 }
98 return data;
99 }
100 }
101
102 return NULL;
103 }
104
105 /* response entity */
106
107 int add_line (char **buffer, char *str)
108 {
109 int len = strlen (*buffer) + strlen (str) + 1;
110 *buffer = realloc (*buffer, len);
111 strcat (*buffer, str);
112 return len;
113 }
114
115 int add_status_line (char **buffer, code_t code)
116 {
117 char tmp[BUFFER_SIZE] = {0};
118
119 /* Status */
120 sprintf (tmp, "%s %s\r\n", HTTP_VERSION, codes[code]);
121 add_line (buffer, tmp);
122
123 return strlen (*buffer);
124 }
125
126 int add_general_header (char **buffer)
127 {
128 char tmp[BUFFER_SIZE] = {0};
129
130 /* Date */
131 time_t ts = time (NULL);
132 sprintf (tmp, "Date: %s\r\n", ctime (&ts));
133 add_line (buffer, tmp);
134
135 /* Pragma */
136
137 return strlen (*buffer);
138 }
139
140 int add_response_header (char **buffer, char *uri)
141 {
142 char tmp[BUFFER_SIZE] = {0};
143
144 /* Location */
145 sprintf (tmp, "Location: %s\r\n", uri);
146 add_line (buffer, tmp);
147
148 /* Server */
149 sprintf (tmp, "Server: %s\r\n", SERVER_NAME);
150 add_line (buffer, tmp);
151
152 /* WWW-Authentificate */
153
154 return strlen (*buffer);
155 }
156
157 int add_entity (char **buffer, unsigned char *entity, int size, char *type, char *encoding)
158 {
159 char tmp[BUFFER_SIZE] = {0};
160 int len = strlen (*buffer);
161
162 /* Allow */
163 /* Expires */
164 /* Last-Modified */
165
166 if (entity != NULL) {
167
168 /* Content-Encoding */
169 if (encoding != NULL) {
170 sprintf (tmp, "Content-Encoding: %s\r\n", encoding);
171 add_line (buffer, tmp);
172 }
173
174 /* Content-Length */
175 sprintf (tmp, "Content-Length: %d\r\n", size);
176 add_line (buffer, tmp);
177
178 /* Content-Type */
179 sprintf (tmp, "Content-Type: %s\r\n", type);
180 add_line (buffer, tmp);
181
182 add_line (buffer, "\r\n");
183
184 /* Entity */
185 len = strlen (*buffer);
186 *buffer = realloc (*buffer, len + size);
187 memcpy (*buffer + len, entity, size);
188 len += size;
189 }
190
191 return len;
192 }
193
194 /* error 400 */
195
196 int error_400 (char **buffer)
197 {
198 add_status_line (buffer, c400);
199 add_general_header (buffer);
200 add_response_header (buffer, NULL);
201
202 char *response = "<html><head><title>Error 400</title></head><body><p>Bad Request</p></body></html>";
203 return add_entity (buffer, (unsigned char *)response, strlen (response), "text/html", "iso-8859-1");
204 }
205
206 /* error 404 */
207
208 int error_404 (char **buffer, char *uri)
209 {
210 add_status_line (buffer, c404);
211 add_general_header (buffer);
212 add_response_header (buffer, uri);
213
214 char *response = "<html><head><title>Error 404</title></head><body><p>File not found</p></body></html>";
215 return add_entity (buffer, (unsigned char *)response, strlen (response), "text/html", "iso-8859-1");
216 }
217
218 /* response html */
219
220 int response_html (char **buffer, char *location, char *response)
221 {
222 add_status_line (buffer, c200);
223 add_general_header (buffer);
224 add_response_header (buffer, location);
225
226 return add_entity (buffer, (unsigned char *)response, strlen (response), "text/html", "iso-8859-1");
227 }
228
229 /* trim string */
230
231 char *trim (char *str)
232 {
233 if (str != NULL) {
234 while ((*str == ' ') || (*str == '\t')) {
235 str++;
236 }
237 }
238 return str;
239 }
240
241 /* main HTTP processing */
242
243 int processing (char *data, int len, char *root, char **pdata)
244 {
245 char *saved_data = data;
246 VERBOSE (DEBUG, PRINT ("Start processing\n"));
247
248 /* check method */
249 char *line = find_sequence (data, len, "\r\n", &data);
250 if (line == NULL) {
251 VERBOSE (WARNING, PRINT ("Unknown received data\n"));
252 if (pdata != NULL) {
253 *pdata = NULL;
254 }
255 return 0;
256 }
257 VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line));
258
259 char *method = strtok (line, " ");
260 char *uri = strtok (NULL, " ");
261 char *version = strtok (NULL, " ");
262 method_t type = not_supported_e;
263 if (strcmp (method, "GET") == 0) {
264 type = get_e;
265 } else if (strcmp ("HEAD", method) == 0) {
266 type = head_e;
267 } else if (strcmp ("POST", method) == 0) {
268 type = post_e;
269 } else {
270 VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
271 free (saved_data);
272 return error_400 (pdata);
273 }
274 VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version));
275
276 /* analyse uri */
277 char *filename = calloc (strlen (root) + strlen (uri) + 2, 1);
278 //sprintf (filename, "%s%s%s", root, ((root[strlen (root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
279 sprintf (filename, "%s/%s", root, uri);
280
281 /* check header */
282 header_t header = {0};
283 while (strcmp (line = find_sequence (data, len, "\r\n", &data), "") != 0) {
284 VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line));
285 char *field = strtok (line, ":");
286 char *value = trim (strtok (NULL, "\r"));
287 VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value));
288 if (*line == 0) {
289 break;
290 }
291
292 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
293 if (strcmp (field, "Allow") == 0) {
294 header.allow = value;
295 } else if (strcmp (field, "Authorization") == 0) {
296 header.authorization = value;
297 } else if (strcmp (field, "Content-Encoding") == 0) {
298 header.content_encoding = value;
299 } else if (strcmp (field, "Content-Length") == 0) {
300 header.content_length = value;
301 } else if (strcmp (field, "Content-Type") == 0) {
302 header.content_type = value;
303 } else if (strcmp (field, "Date") == 0) {
304 header.date = value;
305 } else if (strcmp (field, "Expires") == 0) {
306 header.expires = value;
307 } else if (strcmp (field, "From") == 0) {
308 header.from = value;
309 } else if (strcmp (field, "If-Modified-Since") == 0) {
310 header.if_modified_since = value;
311 } else if (strcmp (field, "Last-Modified") == 0) {
312 header.last_modified = value;
313 } else if (strcmp (field, "Location") == 0) {
314 header.location = value;
315 } else if (strcmp (field, "Pragma") == 0) {
316 header.pragma = value;
317 } else if (strcmp (field, "Referer") == 0) {
318 header.referer = value;
319 } else if (strcmp (field, "Server") == 0) {
320 header.server = value;
321 } else if (strcmp (field, "User-Agent") == 0) {
322 header.user_agent = value;
323 } else if (strcmp (field, "WWW-Authenticate") == 0) {
324 header.www_authenticate = value;
325 } else {
326 VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field));
327 }
328 }
329 VERBOSE (DEBUG, print_header_values (&header));
330
331 /* processing data */
332 if (data) {
333 VERBOSE (DEBUG, PRINT ("Processing data: %s\n", data));
334 }
335
336 /* response */
337 char *buffer = NULL;
338 switch (type) {
339 case get_e:
340 len = readfile ((unsigned char **)&buffer, filename);
341 if (len == 0) {
342 len = error_404 (pdata, "http://localhost/");
343 } else {
344 len = response_html (pdata, "http://localhost/", buffer);
345 free (buffer);
346 }
347 break;
348 case head_e:
349 break;
350 case post_e:
351 break;
352 case not_supported_e:
353 break;
354 }
355
356 /* cleaning */
357 free (saved_data);
358 free (filename);
359
360 return len;
361 }
362
363 /* vim: set ts=4 sw=4 et: */