quick commit 2
[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 VERBOSE (DEBUG, PRINT ("Start processing\n"));
246
247 /* check method */
248 char *line = find_sequence (data, len, "\r\n", &data);
249 if (line == NULL) {
250 VERBOSE (WARNING, PRINT ("Unknown received data\n"));
251 if (pdata != NULL) {
252 *pdata = NULL;
253 }
254 return 0;
255 }
256 VERBOSE (DEBUG, PRINT ("Command line: '%s'\n", line));
257
258 char *method = strtok (line, " ");
259 char *uri = strtok (NULL, " ");
260 char *version = strtok (NULL, " ");
261 method_t type = not_supported_e;
262 if (strcmp (method, "GET") == 0) {
263 type = get_e;
264 } else if (strcmp ("HEAD", method) == 0) {
265 type = head_e;
266 } else if (strcmp ("POST", method) == 0) {
267 type = post_e;
268 } else {
269 VERBOSE (WARNING, PRINT ("Unkown method: %s\n", method));
270 return error_400 (pdata);
271 }
272 VERBOSE (INFO, PRINT ("%s %s (%s)\n", (type == get_e) ? "Get" : (type == head_e) ? "Head" : "Post", uri, version));
273
274 /* analyse uri */
275 char *filename = (char *)calloc (strlen (root) + strlen (uri) + 2, 1);
276 //sprintf (filename, "%s%s%s", root, ((root[strlen (root) - 1] != '/') && (uri[0] != '/')) ? "/" : "", uri);
277 sprintf (filename, "%s/%s", root, uri);
278
279 /* check header */
280 header_t header = {0};
281 while (strcmp (line = find_sequence (data, len, "\r\n", &data), "") != 0) {
282 VERBOSE (DEBUG, PRINT ("Header line: '%s'\n", line));
283 char *field = strtok (line, ":");
284 char *value = trim (strtok (NULL, "\r"));
285 VERBOSE (DEBUG, PRINT ("Field: %s\nValue: %s\n", field, value));
286 if (*line == 0) {
287 break;
288 }
289
290 VERBOSE (DEBUG, PRINT ("Analyse field\n"));
291 if (strcmp (field, "Allow") == 0) {
292 header.allow = value;
293 } else if (strcmp (field, "Authorization") == 0) {
294 header.authorization = value;
295 } else if (strcmp (field, "Content-Encoding") == 0) {
296 header.content_encoding = value;
297 } else if (strcmp (field, "Content-Length") == 0) {
298 header.content_length = value;
299 } else if (strcmp (field, "Content-Type") == 0) {
300 header.content_type = value;
301 } else if (strcmp (field, "Date") == 0) {
302 header.date = value;
303 } else if (strcmp (field, "Expires") == 0) {
304 header.expires = value;
305 } else if (strcmp (field, "From") == 0) {
306 header.from = value;
307 } else if (strcmp (field, "If-Modified-Since") == 0) {
308 header.if_modified_since = value;
309 } else if (strcmp (field, "Last-Modified") == 0) {
310 header.last_modified = value;
311 } else if (strcmp (field, "Location") == 0) {
312 header.location = value;
313 } else if (strcmp (field, "Pragma") == 0) {
314 header.pragma = value;
315 } else if (strcmp (field, "Referer") == 0) {
316 header.referer = value;
317 } else if (strcmp (field, "Server") == 0) {
318 header.server = value;
319 } else if (strcmp (field, "User-Agent") == 0) {
320 header.user_agent = value;
321 } else if (strcmp (field, "WWW-Authenticate") == 0) {
322 header.www_authenticate = value;
323 } else {
324 VERBOSE (WARNING, PRINT ("Unknown header field: '%s'\n", field));
325 }
326 }
327 VERBOSE (DEBUG, print_header_values (&header));
328
329 /* response */
330 char *buffer = NULL;
331 switch (type) {
332 case get_e:
333 VERBOSE (DEBUG, PRINT ("Read file %s\n", filename));
334 len = readfile ((unsigned char **)&buffer, filename);
335 if (len == 0) {
336 len = error_404 (pdata, "http://localhost/");
337 } else {
338 len = response_html (pdata, "http://localhost/", buffer);
339 free (buffer);
340 }
341 break;
342 case head_e:
343 break;
344 case post_e:
345 VERBOSE (DEBUG, PRINT ("Write file %s\n", filename));
346 break;
347 case not_supported_e:
348 break;
349 }
350
351 /* cleaning */
352 VERBOSE (DEBUG, PRINT ("Cleaning\n"));
353 if (filename) {
354 VERBOSE (DEBUG, PRINT ("Cleaning filename\n"));
355 free (filename);
356 }
357
358 return len;
359 }
360
361 /* vim: set ts=4 sw=4 et: */