version 0.1.6
[fms.git] / libs / shttpd / io_emb.c
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 #if defined(EMBEDDED)
14 const char *
15 shttpd_version(void)
16 {
17         return (VERSION);
18 }
19
20 static void
21 call_user(struct conn *c, struct shttpd_arg *arg, shttpd_callback_t func)
22 {
23         arg->priv               = c;
24         arg->state              = c->loc.chan.emb.state;
25         arg->out.buf            = io_space(&c->loc.io);
26         arg->out.len            = io_space_len(&c->loc.io);
27         arg->out.num_bytes      = 0;
28         arg->in.buf             = io_data(&c->rem.io);;
29         arg->in.len             = io_data_len(&c->rem.io);
30         arg->in.num_bytes       = 0;
31
32         func(arg);
33
34         io_inc_head(&c->loc.io, arg->out.num_bytes);
35         io_inc_tail(&c->rem.io, arg->in.num_bytes);
36         c->loc.chan.emb.state = arg->state;             /* Save state */
37
38         /*
39          * If callback finished output, that means it did all cleanup.
40          * If the connection is terminated unexpectedly, we canna call
41          * the callback via the stream close() method from disconnect.
42          * However, if cleanup is already done, we set close() method to
43          * NULL, to prevent the call from disconnect().
44          */
45
46         if (arg->flags & SHTTPD_END_OF_OUTPUT)
47                 c->loc.flags &= ~FLAG_DONT_CLOSE;
48         else
49                 c->loc.flags |= FLAG_DONT_CLOSE;
50 }
51
52 static int
53 do_embedded(struct stream *stream, void *buf, size_t len)
54 {
55         struct shttpd_arg       arg;
56         buf = NULL; len = 0;            /* Squash warnings */
57
58         arg.user_data   = stream->conn->loc.chan.emb.data;
59         arg.flags       = 0;
60
61         call_user(stream->conn, &arg, (shttpd_callback_t)
62                         stream->conn->loc.chan.emb.func.v_func);
63
64         return (0);
65 }
66
67 static void
68 close_embedded(struct stream *stream)
69 {
70         struct shttpd_arg       arg;
71         struct conn             *c = stream->conn;
72
73         arg.flags       = SHTTPD_CONNECTION_ERROR;
74         arg.user_data   = c->loc.chan.emb.data;
75
76         /*
77          * Do not call the user function if SHTTPD_END_OF_OUTPUT was set,
78          * i.e. the callback already terminated correctly
79          */
80         if (stream->flags & FLAG_DONT_CLOSE)
81                 call_user(stream->conn, &arg, (shttpd_callback_t)
82                     c->loc.chan.emb.func.v_func);
83 }
84
85 size_t
86 shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
87 {
88         struct conn     *c = arg->priv;
89         struct io       *io = &c->loc.io;
90         char            *buf = arg->out.buf + arg->out.num_bytes;
91         int             buflen = arg->out.len - arg->out.num_bytes, len = 0;
92         va_list         ap;
93
94         assert(buf <= io->buf + io->size);
95
96         if (buflen > 0) {
97                 va_start(ap, fmt);
98                 len = vsnprintf(buf, buflen, fmt, ap);
99                 va_end(ap);
100
101                 if (len < 0 || len > buflen)
102                         len = buflen;
103                 arg->out.num_bytes += len;
104         }
105
106         return (len);
107 }
108
109 const char *
110 shttpd_get_header(struct shttpd_arg *arg, const char *header_name)
111 {
112         struct conn     *c = arg->priv;
113         char            *p, *s, *e;
114         size_t          len;
115
116         p = c->headers;
117         e = c->request + c->rem.headers_len;
118         len = strlen(header_name);
119
120         while (p < e) {
121                 if ((s = strchr(p, '\n')) != NULL)
122                         s[s[-1] == '\r' ? -1 : 0] = '\0';
123                 if (my_strncasecmp(header_name, p, len) == 0)
124                         return (p + len + 2);
125
126                 p += strlen(p) + 1;
127         }
128
129         return (NULL);
130 }
131
132 const char *
133 shttpd_get_env(struct shttpd_arg *arg, const char *env_name)
134 {
135         struct conn     *c = arg->priv;
136         struct vec      *vec;
137
138         if (strcmp(env_name, "REQUEST_METHOD") == 0) {
139                 return (known_http_methods[c->method].ptr);
140         } else if (strcmp(env_name, "REQUEST_URI") == 0) {
141                 return (c->uri);
142         } else if (strcmp(env_name, "QUERY_STRING") == 0) {
143                 return (c->query);
144         } else if (strcmp(env_name, "REMOTE_USER") == 0) {
145                 vec = &c->ch.user.v_vec;
146                 if (vec->len > 0) {
147                         ((char *) vec->ptr)[vec->len] = '\0';
148                         return (vec->ptr);
149                 }
150         } else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
151                 return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
152         }
153
154         return (NULL);
155 }
156
157 void
158 shttpd_get_http_version(struct shttpd_arg *arg, unsigned long *major, unsigned long *minor)
159 {
160         struct conn *c = arg->priv;
161         
162         *major = c->major_version;
163         *minor = c->minor_version;
164 }
165
166 void
167 shttpd_register_uri(struct shttpd_ctx *ctx,
168                 const char *uri, shttpd_callback_t callback, void *data)
169 {
170         struct registered_uri   *e;
171
172         if ((e = malloc(sizeof(*e))) != NULL) {
173                 e->uri                  = my_strdup(uri);
174                 e->callback.v_func      = (void (*)(void)) callback;
175                 e->callback_data        = data;
176                 LL_TAIL(&ctx->registered_uris, &e->link);
177         }
178 }
179
180 #if 0
181 struct shttpd_ctx *
182 shttpd_init2(const char *config_file, char *names[], char *values[], size_t n)
183 {
184         size_t i;
185
186         for (i = 0; i < n; i++)
187                 set_option(names[i], values[i]);
188
189         return (init_ctx(config_file, 0, NULL));
190 }
191 #endif
192
193 void
194 shttpd_protect_uri(struct shttpd_ctx *ctx, const char *uri, const char *file)
195 {
196         struct uri_auth *auth;
197
198         if ((auth = malloc(sizeof(*auth))) != NULL) {
199                 auth->uri       = my_strdup(uri);
200                 auth->file_name = my_strdup(file);
201                 auth->uri_len   = strlen(uri);
202                 LL_ADD(&ctx->uri_auths, &auth->link);
203         }
204 }
205
206 int
207 shttpd_get_var(const char *var, const char *buf, int buf_len,
208                 char *value, int value_len)
209 {
210         const char      *p, *e, *s;
211         size_t          var_len;
212
213         var_len = strlen(var);
214         e = buf + buf_len - var_len;
215
216         /* buf is "var1=val1&var2=val2...". Find variable first */
217         for (p = buf; p < e; p++)
218                 if (!my_strncasecmp(var, p, var_len) && p[var_len] == '=') {
219
220                         /* Found. Shift to variable value */
221                         e = buf + buf_len;
222                         p += var_len + 1;
223
224                         /* Find where the value ends */
225                         if ((s = memchr(p, '&', e - p)) == NULL)
226                                 s = e;
227
228                         /* Copy value into the buffer, decoding it */
229                         url_decode(p, s - p, value, value_len);
230
231                         return (1);
232                 }
233
234         return (0);
235 }
236
237 static int
238 match_regexp(const char *regexp, const char *text)
239 {
240         if (*regexp == '\0')
241                 return (*text == '\0');
242
243         if (*regexp == '*')
244                 do {
245                         if (match_regexp(regexp + 1, text))
246                                 return (1);
247                 } while (*text++ != '\0');
248
249         if (*text != '\0' && *regexp == *text)
250                 return (match_regexp(regexp + 1, text + 1));
251
252         return (0);
253 }
254
255 struct registered_uri *
256 is_registered_uri(struct shttpd_ctx *ctx, const char *uri)
257 {
258         struct llhead           *lp;
259         struct registered_uri   *reg_uri;
260
261         LL_FOREACH(&ctx->registered_uris, lp) {
262                 reg_uri = LL_ENTRY(lp, struct registered_uri, link);
263                 if (match_regexp(reg_uri->uri, uri))
264                         return (reg_uri);
265         }
266
267         return (NULL);
268 }
269
270 void
271 setup_embedded_stream(struct conn *c, union variant func, void *data)
272 {
273         c->loc.chan.emb.state = NULL;
274         c->loc.chan.emb.func = func;
275         c->loc.chan.emb.data = data;
276         c->loc.io_class = &io_embedded;
277         c->loc.flags |= FLAG_R | FLAG_W |FLAG_ALWAYS_READY;
278 }
279
280 void
281 shttpd_handle_error(struct shttpd_ctx *ctx, int code,
282                 shttpd_callback_t func, void *data)
283 {
284         struct error_handler    *e;
285
286         if ((e = malloc(sizeof(*e))) != NULL) {
287                 e->code = code;
288                 e->callback.v_func = (void (*)(void)) func;
289                 e->callback_data = data;
290                 LL_TAIL(&ctx->error_handlers, &e->link);
291         }
292 }
293
294 const struct io_class   io_embedded =  {
295         "embedded",
296         do_embedded,
297         (int (*)(struct stream *, const void *, size_t)) do_embedded,
298         close_embedded
299 };
300
301 #endif /* EMBEDDED */