version 0.2.1
[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 const char *
14 shttpd_version(void)
15 {
16         return (VERSION);
17 }
18
19 static void
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
57 static int
58 do_embedded(struct stream *stream, void *buf, size_t len)
59 {
60         struct shttpd_arg       arg;
61         buf = NULL; len = 0;            /* Squash warnings */
62
63         arg.user_data   = stream->conn->loc.chan.emb.data;
64         arg.flags       = 0;
65
66         call_user(stream->conn, &arg, (shttpd_callback_t)
67                         stream->conn->loc.chan.emb.func.v_func);
68
69         return (0);
70 }
71
72 static void
73 close_embedded(struct stream *stream)
74 {
75         struct shttpd_arg       arg;
76         struct conn             *c = stream->conn;
77
78         arg.flags       = SHTTPD_CONNECTION_ERROR;
79         arg.user_data   = c->loc.chan.emb.data;
80
81         /*
82          * Do not call the user function if SHTTPD_END_OF_OUTPUT was set,
83          * i.e. the callback already terminated correctly
84          */
85         if (stream->flags & FLAG_DONT_CLOSE)
86                 call_user(stream->conn, &arg, (shttpd_callback_t)
87                     c->loc.chan.emb.func.v_func);
88 }
89
90 size_t
91 shttpd_printf(struct shttpd_arg *arg, const char *fmt, ...)
92 {
93         struct conn     *c = arg->priv;
94         struct io       *io = &c->loc.io;
95         char            *buf = arg->out.buf + arg->out.num_bytes;
96         int             buflen = arg->out.len - arg->out.num_bytes, len = 0;
97         va_list         ap;
98
99         assert(buf <= io->buf + io->size);
100
101         if (buflen > 0) {
102                 va_start(ap, fmt);
103                 len = vsnprintf(buf, buflen, fmt, ap);
104                 va_end(ap);
105
106                 if (len < 0 || len > buflen)
107                         len = buflen;
108                 arg->out.num_bytes += len;
109         }
110
111         return (len);
112 }
113
114 const char *
115 shttpd_get_header(struct shttpd_arg *arg, const char *header_name)
116 {
117         struct conn     *c = arg->priv;
118         char            *p, *s, *e;
119         size_t          len;
120
121         p = c->headers;
122         e = c->request + c->rem.headers_len;
123         len = strlen(header_name);
124
125         while (p < e) {
126                 if ((s = strchr(p, '\n')) != NULL)
127                         s[s[-1] == '\r' ? -1 : 0] = '\0';
128                 if (my_strncasecmp(header_name, p, len) == 0)
129                         return (p + len + 2);
130
131                 p += strlen(p) + 1;
132         }
133
134         return (NULL);
135 }
136
137 const char *
138 shttpd_get_env(struct shttpd_arg *arg, const char *env_name)
139 {
140         struct conn     *c = arg->priv;
141         struct vec      *vec;
142
143         if (strcmp(env_name, "REQUEST_METHOD") == 0) {
144                 return (known_http_methods[c->method].ptr);
145         } else if (strcmp(env_name, "REQUEST_URI") == 0) {
146                 return (c->uri);
147         } else if (strcmp(env_name, "QUERY_STRING") == 0) {
148                 return (c->query);
149         } else if (strcmp(env_name, "REMOTE_USER") == 0) {
150                 vec = &c->ch.user.v_vec;
151                 if (vec->len > 0) {
152                         ((char *) vec->ptr)[vec->len] = '\0';
153                         return (vec->ptr);
154                 }
155         } else if (strcmp(env_name, "REMOTE_ADDR") == 0) {
156                 return (inet_ntoa(c->sa.u.sin.sin_addr));/* FIXME NOT MT safe */
157         }
158
159         return (NULL);
160 }
161
162 void
163 shttpd_get_http_version(struct shttpd_arg *arg, 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
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                  = my_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
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                     !my_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 (url_decode(p, s - p, value, value_len));
208                 }
209
210         return (-1);
211 }
212
213 static int
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 *
232 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
247 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 = &io_embedded;
253         c->loc.flags |= FLAG_R | FLAG_W |FLAG_ALWAYS_READY;
254 }
255
256 void
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 const struct io_class   io_embedded =  {
271         "embedded",
272         do_embedded,
273         (int (*)(struct stream *, const void *, size_t)) do_embedded,
274         close_embedded
275 };