1 /*
2  * Copyright (c) 2004-2005 Sergey Lyubka <valenok@gmail.com>
3  * All rights reserved
4  *
5  * "THE BEER-WARE LICENSE" (Revision 42):
6  * Sergey Lyubka wrote this file.  As long as you retain this notice you
7  * can do whatever you want with this stuff. If we meet some day, and you think
8  * this stuff is worth it, you can buy me a beer in return.
9  */
10 
11 #include "defs.h"
12 
13 const char *
shttpd_version(void)14 shttpd_version(void)
15 {
16 	return (VERSION);
17 }
18 
19 static void
call_user(struct conn * c,struct shttpd_arg * arg,shttpd_callback_t func)20 call_user(struct conn *c, struct shttpd_arg *arg, shttpd_callback_t func)
21 {
22 	arg->priv		= c;
23 	arg->state		= c->loc.chan.emb.state;
24 	arg->out.buf		= io_space(&c->loc.io);
25 	arg->out.len		= io_space_len(&c->loc.io);
26 	arg->out.num_bytes	= 0;
27 	arg->in.buf		= io_data(&c->rem.io);;
28 	arg->in.len		= io_data_len(&c->rem.io);
29 	arg->in.num_bytes	= 0;
30 
31 	if (io_data_len(&c->rem.io) >= c->rem.io.size)
32 		arg->flags |= SHTTPD_POST_BUFFER_FULL;
33 
34 	if (c->rem.content_len > 0 && c->rem.io.total < c->rem.content_len)
35 		arg->flags |= SHTTPD_MORE_POST_DATA;
36 
37 	func(arg);
38 
39 	io_inc_head(&c->loc.io, arg->out.num_bytes);
40 	io_inc_tail(&c->rem.io, arg->in.num_bytes);
41 	c->loc.chan.emb.state = arg->state;		/* Save state */
42 
43 	/*
44 	 * If callback finished output, that means it did all cleanup.
45 	 * If the connection is terminated unexpectedly, we canna call
46 	 * the callback via the stream close() method from disconnect.
47 	 * However, if cleanup is already done, we set close() method to
48 	 * NULL, to prevent the call from disconnect().
49 	 */
50 
51 	if (arg->flags & SHTTPD_END_OF_OUTPUT)
52 		c->loc.flags &= ~FLAG_DONT_CLOSE;
53 	else
54 		c->loc.flags |= FLAG_DONT_CLOSE;
55 
56 	if (arg->flags & SHTTPD_SUSPEND)
57 		c->loc.flags |= FLAG_SUSPEND;
58 }
59 
60 static int
do_embedded(struct stream * stream,void * buf,size_t len)61 do_embedded(struct stream *stream, void *buf, size_t len)
62 {
63 	struct shttpd_arg	arg;
64 	buf = NULL; len = 0;		/* Squash warnings */
65 
66 	arg.user_data	= stream->conn->loc.chan.emb.data;
67 	arg.flags	= 0;
68 
69 	call_user(stream->conn, &arg, (shttpd_callback_t)
70 			stream->conn->loc.chan.emb.func.v_func);
71 
72 	return (0);
73 }
74 
75 static void
close_embedded(struct stream * stream)76 close_embedded(struct stream *stream)
77 {
78 	struct shttpd_arg	arg;
79 	struct conn		*c = stream->conn;
80 
81 	arg.flags	= SHTTPD_CONNECTION_ERROR;
82 	arg.user_data	= c->loc.chan.emb.data;
83 
84 	/*
85 	 * Do not call the user function if SHTTPD_END_OF_OUTPUT was set,
86 	 * i.e. the callback already terminated correctly
87 	 */
88 	if (stream->flags & FLAG_DONT_CLOSE)
89 		call_user(stream->conn, &arg, (shttpd_callback_t)
90 		    c->loc.chan.emb.func.v_func);
91 }
92 
93 size_t
shttpd_printf(struct shttpd_arg * arg,const char * fmt,...)94 shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
95 {
96 	char		*buf = arg->out.buf + arg->out.num_bytes;
97 	int		buflen = arg->out.len - arg->out.num_bytes, len = 0;
98 	va_list		ap;
99 
100 	if (buflen > 0) {
101 		va_start(ap, fmt);
102 		len = vsnprintf(buf, buflen, fmt, ap);
103 		va_end(ap);
104 
105 		if (len < 0 || len > buflen)
106 			len = buflen;
107 		arg->out.num_bytes += len;
108 	}
109 
110 	return (len);
111 }
112 
113 const char *
shttpd_get_header(struct shttpd_arg * arg,const char * header_name)114 shttpd_get_header(struct shttpd_arg *arg, const char *header_name)
115 {
116 	struct conn	*c = arg->priv;
117 	char		*p, *s, *e;
118 	size_t		len;
119 
120 	p = c->headers;
121 	e = c->request + c->rem.headers_len;
122 	len = strlen(header_name);
123 
124 	while (p < e) {
125 		if ((s = strchr(p, '\n')) != NULL)
126 			s[s[-1] == '\r' ? -1 : 0] = '\0';
127 		if (_shttpd_strncasecmp(header_name, p, len) == 0)
128 			return (p + len + 2);
129 
130 		p += strlen(p) + 1;
131 	}
132 
133 	return (NULL);
134 }
135 
136 const char *
shttpd_get_env(struct shttpd_arg * arg,const char * env_name)137 shttpd_get_env(struct shttpd_arg *arg, const char *env_name)
138 {
139 	struct conn	*c = arg->priv;
140 	struct vec	*vec;
141 
142 	if (strcmp(env_name, "REQUEST_METHOD") == 0) {
143 		return (_shttpd_known_http_methods[c->method].ptr);
144 	} else if (strcmp(env_name, "REQUEST_URI") == 0) {
145 		return (c->uri);
146 	} else if (strcmp(env_name, "QUERY_STRING") == 0) {
147 		return (c->query);
148 	} else if (strcmp(env_name, "REMOTE_USER") == 0) {
149 		vec = &c->ch.user.v_vec;
150 		if (vec->len > 0) {
151 			((char *) vec->ptr)[vec->len] = '\0';
152 			return (vec->ptr);
153 		}
154 	} else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
155 		return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
156 	}
157 
158 	return (NULL);
159 }
160 
161 void
shttpd_get_http_version(struct shttpd_arg * arg,unsigned long * major,unsigned long * minor)162 shttpd_get_http_version(struct shttpd_arg *arg,
163 		unsigned long *major, unsigned long *minor)
164 {
165 	struct conn *c = arg->priv;
166 
167 	*major = c->major_version;
168 	*minor = c->minor_version;
169 }
170 
171 void
shttpd_register_uri(struct shttpd_ctx * ctx,const char * uri,shttpd_callback_t callback,void * data)172 shttpd_register_uri(struct shttpd_ctx *ctx,
173 		const char *uri, shttpd_callback_t callback, void *data)
174 {
175 	struct registered_uri	*e;
176 
177 	if ((e = malloc(sizeof(*e))) != NULL) {
178 		e->uri			= _shttpd_strdup(uri);
179 		e->callback.v_func	= (void (*)(void)) callback;
180 		e->callback_data	= data;
181 		LL_TAIL(&ctx->registered_uris, &e->link);
182 	}
183 }
184 
185 int
shttpd_get_var(const char * var,const char * buf,int buf_len,char * value,int value_len)186 shttpd_get_var(const char *var, const char *buf, int buf_len,
187 		char *value, int value_len)
188 {
189 	const char	*p, *e, *s;
190 	size_t		var_len;
191 
192 	var_len = strlen(var);
193 	e = buf + buf_len;		/* End of QUERY_STRING buffer	*/
194 
195 	/* buf is "var1=val1&var2=val2...". Find variable first */
196 	for (p = buf; p + var_len < e; p++)
197 		if ((p == buf || p[-1] == '&') &&
198 		    p[var_len] == '=' &&
199 		    !_shttpd_strncasecmp(var, p, var_len)) {
200 
201 			/* Point 'p' to var value, 's' to the end of value */
202 			p += var_len + 1;
203 			if ((s = memchr(p, '&', e - p)) == NULL)
204 				s = e;
205 
206 			/* URL-decode value. Return result length */
207 			return (_shttpd_url_decode(p, s - p, value, value_len));
208 		}
209 
210 	return (-1);
211 }
212 
213 static int
match_regexp(const char * regexp,const char * text)214 match_regexp(const char *regexp, const char *text)
215 {
216 	if (*regexp == '\0')
217 		return (*text == '\0');
218 
219 	if (*regexp == '*')
220 		do {
221 			if (match_regexp(regexp + 1, text))
222 				return (1);
223 		} while (*text++ != '\0');
224 
225 	if (*text != '\0' && *regexp == *text)
226 		return (match_regexp(regexp + 1, text + 1));
227 
228 	return (0);
229 }
230 
231 struct registered_uri *
_shttpd_is_registered_uri(struct shttpd_ctx * ctx,const char * uri)232 _shttpd_is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
233 {
234 	struct llhead		*lp;
235 	struct registered_uri	*reg_uri;
236 
237 	LL_FOREACH(&ctx->registered_uris, lp) {
238 		reg_uri = LL_ENTRY(lp, struct registered_uri, link);
239 		if (match_regexp(reg_uri->uri, uri))
240 			return (reg_uri);
241 	}
242 
243 	return (NULL);
244 }
245 
246 void
_shttpd_setup_embedded_stream(struct conn * c,union variant func,void * data)247 _shttpd_setup_embedded_stream(struct conn *c, union variant func, void *data)
248 {
249 	c->loc.chan.emb.state = NULL;
250 	c->loc.chan.emb.func = func;
251 	c->loc.chan.emb.data = data;
252 	c->loc.io_class = &_shttpd_io_embedded;
253 	c->loc.flags |= FLAG_R | FLAG_W |FLAG_ALWAYS_READY;
254 }
255 
256 void
shttpd_handle_error(struct shttpd_ctx * ctx,int code,shttpd_callback_t func,void * data)257 shttpd_handle_error(struct shttpd_ctx *ctx, int code,
258 		shttpd_callback_t func, void *data)
259 {
260 	struct error_handler	*e;
261 
262 	if ((e = malloc(sizeof(*e))) != NULL) {
263 		e->code = code;
264 		e->callback.v_func = (void (*)(void)) func;
265 		e->callback_data = data;
266 		LL_TAIL(&ctx->error_handlers, &e->link);
267 	}
268 }
269 
270 void
shttpd_wakeup(const void * priv)271 shttpd_wakeup(const void *priv)
272 {
273 	const struct conn	*conn = priv;
274 	char			buf[sizeof(int) + sizeof(void *)];
275 	int			cmd = CTL_WAKEUP;
276 
277 #if 0
278 	conn->flags &= ~SHTTPD_SUSPEND;
279 #endif
280 	(void) memcpy(buf, &cmd, sizeof(cmd));
281 	(void) memcpy(buf + sizeof(cmd), conn, sizeof(conn));
282 
283 	(void) send(conn->worker->ctl[1], buf, sizeof(buf), 0);
284 }
285 
286 const struct io_class	_shttpd_io_embedded =  {
287 	"embedded",
288 	do_embedded,
289 	(int (*)(struct stream *, const void *, size_t)) do_embedded,
290 	close_embedded
291 };
292