quick commit
[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
62/* print header values */
63
64void 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
86char *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
8a1d9e4a 107int add_line (char **buffer, char *str)
bb0468a5 108{
8a1d9e4a
LM
109 int len = strlen (*buffer) + strlen (str) + 1;
110 *buffer = realloc (*buffer, len);
111 strcat (*buffer, str);
d0b0d52b
ML
112 return len;
113}
114
8a1d9e4a 115int add_status_line (char **buffer, code_t code)
bb0468a5 116{
8a1d9e4a
LM
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);
bb0468a5
LM
124}
125
8a1d9e4a 126int add_general_header (char **buffer)
bb0468a5 127{
8a1d9e4a
LM
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
140int 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);
bb0468a5
LM
155}
156
8a1d9e4a 157int add_entity (char **buffer, unsigned char *entity, int size, char *type, char *encoding)
bb0468a5 158{
8a1d9e4a
LM
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
bb0468a5
LM
191 return len;
192}
193
194/* error 400 */
195
196int error_400 (char **buffer)
197{
8a1d9e4a
LM
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
4baf6839
LM
206/* error 404 */
207
208int 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
8a1d9e4a
LM
218/* response html */
219
220int 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");
bb0468a5
LM
227}
228
229/* trim string */
230
231char *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
4baf6839 243int processing (char *data, int len, char *root, char **pdata)
bb0468a5
LM
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));
bb0468a5
LM
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
4baf6839
LM
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
bb0468a5
LM
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
bb0468a5 336 /* response */
4baf6839 337 char *buffer = NULL;
8a1d9e4a
LM
338 switch (type) {
339 case get_e:
4baf6839
LM
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 }
8a1d9e4a
LM
347 break;
348 case head_e:
349 break;
350 case post_e:
351 break;
352 case not_supported_e:
353 break;
354 }
355
4baf6839
LM
356 /* cleaning */
357 free (saved_data);
358 free (filename);
359
8a1d9e4a 360 return len;
bb0468a5
LM
361}
362
d0b0d52b 363/* vim: set ts=4 sw=4 et: */