mmlib/mmat: replace some variables by literal constants
[mmondor.git] / tests / bktr_httpd / http.c
1 /* $Id: http.c,v 1.4 2008/01/29 01:41:49 mmondor Exp $ */
2
3 /*
4 * Copyright (c) 2008, Matthew Mondor
5 * ALL RIGHTS RESERVED.
6 */
7
8
9 #include <ctype.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <syslog.h>
13
14 #include "types.h"
15 #include "http.h"
16 #include "client.h"
17 #include "sendq.h"
18 #include "config.h"
19
20
21 static int http_straspl(char **, char *, int);
22 static int http_strspl(char **, char *, int, int);
23 static int http_validate(http_t *);
24
25
26 const char *http_status_string[HTSTAT_MAX] = {
27 "HTSTAT_CONTINUE",
28 "HTSTAT_OVERFLOW",
29 "HTSTAT_BADREQUEST",
30 "HTSTAT_BADMETHOD",
31 "HTSTAT_BADPATH",
32 "HTSTAT_BADENCODING",
33 "HTSTAT_DONE"
34 };
35
36 const struct http_stat_table http_status_table[HTSTAT_MAX] = {
37 { 200, "OK" },
38 { 413, "Request entity too large" },
39 { 400, "Bad request" },
40 { 501, "Method not implemented" },
41 { 404, "Object not found" },
42 { 501, "Transfer encoding not implemented" },
43 { 401, "Unauthorized" },
44 { 200, "OK" }
45 };
46
47
48 void
49 http_init(http_t *h)
50 {
51
52 h->reqlen = 0;
53 h->first = h->firstheader = true;
54 *h->path = '\0';
55 *h->method = '\0';
56 *h->auth = '\0';
57 (void) strcpy(h->lang, "en");
58 h->host = false;
59 }
60
61 /*
62 * Splits columns of a string delimited by spaces and/or tabs, and fills
63 * char **argv with pointers to each column. Returns the number of columns
64 * that could be filled in. The supplied string IS modified.
65 */
66 static int
67 http_straspl(char **argv, char *str, int maxcols)
68 {
69 register char *ptr, *ptr2;
70 int col;
71
72 for (ptr = str, col = 0; *ptr != '\0' && col < maxcols; ) {
73 for ( ; *ptr == ' ' || *ptr == '\t'; ptr++) ;
74 if (*ptr != '\0') {
75 for (ptr2 = ptr;
76 *ptr != '\0' && *ptr != ' ' && *ptr != '\t';
77 ptr++) ;
78 if (ptr != ptr2) {
79 if (*ptr != '\0')
80 *ptr++ = '\0';
81 argv[col++] = ptr2;
82 } else
83 break;
84 } else
85 break;
86 }
87
88 return col;
89 }
90
91 static int
92 http_strspl(char **argv, char *str, int maxcols, int sep)
93 {
94 register char *ptr, *ptr2;
95 int col;
96
97 for (col = 0, ptr = str; *ptr != '\0' && col < maxcols; ) {
98 for (ptr2 = ptr; *ptr != '\0' && (int)*ptr != sep; ptr++) ;
99 if (*ptr != '\0')
100 *ptr++ = '\0';
101 argv[col++] = ptr2;
102 }
103
104 return col;
105 }
106
107 int
108 http_parseline(http_t *h, char *line, size_t linelen)
109 {
110 char *ptr, *tptr;
111
112 if (h->reqlen + linelen + 2 > REQUEST_MAXSIZE)
113 return HTSTAT_OVERFLOW;
114 h->reqlen += linelen + 2;
115
116 if (h->first) {
117 char *col[4];
118 int cols;
119
120 if (linelen == 0)
121 return HTSTAT_CONTINUE;
122
123 h->first = false;
124
125 /* This line should have the method. */
126 if ((cols = http_straspl(col, line, 4)) == 2) {
127 /* Old style HTTP request (method path) */
128 (void) strncpy(h->method, col[0], HTTP_METHOD_MAX - 1);
129 (void) strncpy(h->path, col[1], HTTP_PATH_MAX - 1);
130 h->proto = 0;
131 return http_validate(h);
132 } else if (cols == 3) {
133 float f;
134
135 /* New style HTTP request (method path HTTP/version) */
136 (void) strncpy(h->method, col[0], HTTP_METHOD_MAX - 1);
137 (void) strncpy(h->path, col[1], HTTP_PATH_MAX - 1);
138 if ((cols = http_strspl(col, col[2], 2, '/')) != 2)
139 return HTSTAT_BADREQUEST;
140 if (strcasecmp(col[0], "HTTP") != 0)
141 return HTSTAT_BADREQUEST;
142 if ((f = strtof(col[1], NULL)) == 1.0f)
143 h->proto = 10;
144 else
145 h->proto = 11;
146 return HTSTAT_CONTINUE;
147 }
148
149 return HTSTAT_BADREQUEST;
150 }
151
152 if (linelen == '\0')
153 return http_validate(h);
154
155 /*
156 * Parse header line.
157 * If a line begins with whitespace, it is supposed to be the
158 * continuation of a previous one, which must exist.
159 * We ignore any continuations.
160 */
161
162 if (*line == '\t' || *line == ' ') {
163 if (h->firstheader)
164 return HTSTAT_BADREQUEST;
165 return HTSTAT_CONTINUE;
166 }
167 h->firstheader = false;
168
169 if ((tptr = strstr(line, ": ")) == NULL)
170 return HTSTAT_BADREQUEST;
171 for (ptr = line; ptr < tptr; ptr++) {
172 if (isspace((int)*ptr))
173 return HTSTAT_BADREQUEST;
174 *ptr = tolower((int)*ptr);
175 }
176 *tptr = '\0';
177 tptr += 2;
178 if (strcmp(line, "host") == 0) {
179 if (h->host)
180 return HTSTAT_BADREQUEST;
181 h->host = true;
182 return HTSTAT_CONTINUE;
183 }
184 if (strcmp(line, "accept-language") == 0) {
185 if (isalpha((int)tptr[0]) && isalpha((int)tptr[1])) {
186 h->lang[0] = tolower((int)tptr[0]);
187 h->lang[1] = tolower((int)tptr[1]);
188 return HTSTAT_CONTINUE;
189 }
190 return HTSTAT_BADREQUEST;
191 }
192 if (strcmp(line, "transfer-encoding") == 0) {
193 if (strcasecmp(tptr, "chunked") == 0)
194 return HTSTAT_BADENCODING;
195 return HTSTAT_CONTINUE;
196 }
197 if (strcmp(line, "authorization") == 0) {
198 char *col[3];
199 int cols;
200
201 if (*h->auth != '\0')
202 return HTSTAT_BADREQUEST;
203 if ((cols = http_strspl(col, tptr, 2, ' ')) != 2)
204 return HTSTAT_BADREQUEST;
205 if (strcasecmp(col[0], "Basic") != 0)
206 return HTSTAT_BADREQUEST;
207 (void) strncpy(h->auth, col[1], HTTP_AUTH_MAX - 1);
208 return HTSTAT_CONTINUE;
209 }
210
211 return HTSTAT_CONTINUE;
212 }
213
214 static int
215 http_validate(http_t *h)
216 {
217
218 if (h->proto == 11 && !h->host)
219 return HTSTAT_BADREQUEST;
220
221 if (strcmp(h->method, "GET") != 0)
222 return HTSTAT_BADMETHOD;
223
224 if (strcmp(h->path, "/") == 0)
225 h->stream = false;
226 else if (strcmp(h->path, "/stream") == 0)
227 h->stream = true;
228 else
229 return HTSTAT_BADPATH;
230
231 if (h->auth == '\0' || strcmp(h->auth, AUTH_TOKEN) != 0)
232 return HTSTAT_AUTHREQUIRED;
233
234 /* XXX Perform language check and revert to "en" if not supported */
235
236 return HTSTAT_DONE;
237 }
238
239 bool
240 http_reply(list_t *q, http_t *h, bool keepalive, int code,
241 const char *desc, const char *mimetype, const char *data)
242 {
243
244 if (h->proto == 0)
245 return sendq_page(q, "%s\r\n", data);
246
247 return sendq_page(q,
248 "HTTP/1.1 %d %s\r\n"
249 "Server: " SERVER_VERSION "\r\n"
250 "Connection: %s\r\n"
251 "Cache-Control: no-cache\r\n"
252 "%s"
253 "Content-Length: %d\r\n"
254 "Content-Type: %s\r\n"
255 "\r\n"
256 "%s\r\n",
257 code, desc,
258 (keepalive ? "keep-alive" : "close"),
259 (code == 401 ?
260 "WWW-Authenticate: Basic realm=\"" AUTH_REALM "\"\r\n" : ""),
261 strlen(data) + 2,
262 (mimetype == NULL ? "text/html; charset=utf-8" : mimetype),
263 data);
264 }
265
266 bool
267 http_error(client_t *c, int code, const char *desc)
268 {
269
270 syslog(LOG_NOTICE,
271 "Error: Address=%s, Code=%d, Method=[%s], Path=[%s], Auth=[%s]",
272 c->netaddr->address, code, c->http.method, c->http.path,
273 c->http.auth);
274
275 /* XXX We actually still need fmt since we want to provide some HTML */
276 return http_reply(&c->sendq, &c->http, false, code, desc,
277 "text/plain; charset=us-ascii", desc);
278 }