version 0.2.1
[fms.git] / libs / shttpd / config.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 static int isbyte(int n) { return (n >= 0 && n <= 255); }
14
15 static void
16 set_acl(struct shttpd_ctx *ctx, const char *s)
17 {
18         struct acl      *acl = NULL;
19         char            flag;
20         int             len, a, b, c, d, n, mask;
21         struct llhead   *lp, *tmp;
22
23         /* Delete the old ACLs if any */
24         LL_FOREACH_SAFE(&ctx->acl, lp, tmp)
25                 free(LL_ENTRY(lp, struct acl, link));
26
27         FOR_EACH_WORD_IN_LIST(s, len) {
28
29                 mask = 32;
30
31                 if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
32                         elog(E_FATAL, NULL, "[%s]: subnet must be "
33                             "[+|-]x.x.x.x[/x]", s);
34                 } else if (flag != '+' && flag != '-') {
35                         elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
36                 } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
37                         elog(E_FATAL, NULL, "bad ip address: [%s]", s);
38                 } else  if ((acl = malloc(sizeof(*acl))) == NULL) {
39                         elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
40                 } else if (sscanf(s + n, "/%d", &mask) == 0) { 
41                         /* Do nothing, no mask specified */
42                 } else if (mask < 0 || mask > 32) {
43                         elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
44                 }
45
46                 acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
47                 acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
48                 acl->flag = flag;
49                 LL_TAIL(&ctx->acl, &acl->link);
50         }
51 }
52
53 #ifndef NO_SSL
54 /*
55  * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
56  */
57 static void
58 set_ssl(struct shttpd_ctx *ctx, const char *pem)
59 {
60         SSL_CTX         *CTX;
61         void            *lib;
62         struct ssl_func *fp;
63
64         /* Load SSL library dynamically */
65         if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL)
66                 elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB);
67
68         for (fp = ssl_sw; fp->name != NULL; fp++)
69                 if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL)
70                         elog(E_FATAL, NULL,"set_ssl: cannot find %s", fp->name);
71
72         /* Initialize SSL crap */
73         SSL_library_init();
74
75         if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
76                 elog(E_FATAL, NULL, "SSL_CTX_new error");
77         else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
78                 elog(E_FATAL, NULL, "cannot open %s", pem);
79         else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
80                 elog(E_FATAL, NULL, "cannot open %s", pem);
81         ctx->ssl_ctx = CTX;
82 }
83 #endif /* NO_SSL */
84
85 static void
86 open_log_file(FILE **fpp, const char *path)
87 {
88         if (*fpp != NULL)
89                 (void) fclose(*fpp);
90
91         if (path == NULL) {
92                 *fpp = NULL;
93         } else if ((*fpp = fopen(path, "a")) == NULL) {
94                 elog(E_FATAL, NULL, "cannot open log file %s: %s",
95                     path, strerror(errno));
96         }
97 }
98
99 static void
100 set_alog(struct shttpd_ctx *ctx, const char *path)
101 {
102         open_log_file(&ctx->access_log, path);
103 }
104
105 static void
106 set_elog(struct shttpd_ctx *ctx, const char *path)
107 {
108         open_log_file(&ctx->error_log, path);
109 }
110
111 static void show_cfg_page(struct shttpd_arg *arg);
112
113 static void
114 set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
115 {
116         free_list(&ctx->registered_uris, &registered_uri_destructor);
117
118         if (uri != NULL) {
119                 shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
120         }
121 }
122
123 static void
124 set_ports(struct shttpd_ctx *ctx, const char *p)
125 {
126         int             len, is_ssl;
127
128         free_list(&ctx->listeners, &listener_destructor);
129
130         FOR_EACH_WORD_IN_LIST(p, len) {
131                 is_ssl = p[len - 1] == 's' ? 1 : 0;
132                 if (shttpd_listen(ctx, atoi(p), is_ssl) == -1)
133                         elog(E_FATAL, NULL,
134                             "Cannot open socket on port %d", atoi(p));
135         }
136 }
137
138 static const struct opt {
139         int             index;          /* Index in shttpd_ctx          */
140         const char      *name;          /* Option name in config file   */
141         const char      *description;   /* Description                  */
142         const char      *default_value; /* Default option value         */
143         void (*setter)(struct shttpd_ctx *, const char *);
144 } known_options[] = {
145         {OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
146         {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
147         {OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
148         {OPT_DIR_LIST, "dir_list", "Directory listing", "1", NULL},
149         {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
150         {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
151 #ifndef NO_CGI
152         {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
153         {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
154         {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
155 #endif /* NO_CGI */
156         {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
157 #ifndef NO_AUTH
158         {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
159         {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
160         {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
161 #endif /* !NO_AUTH */
162         {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
163         {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
164         {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
165 #ifndef NO_SSL
166         {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
167 #endif /* NO_SSL */
168         {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
169         {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
170 #ifdef _WIN32
171 #else
172         {OPT_INETD, "inetd", "Inetd mode", "0", NULL},
173         {OPT_UID, "uid", "\tRun as user", NULL, NULL},
174 #endif /* _WIN32 */
175         {-1, NULL, NULL, NULL, NULL}
176 };
177
178 void shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
179 {
180         const struct opt        *o;
181
182         for (o = known_options; o->name != NULL; o++)
183                 if (!strcmp(opt, o->name))
184                         break;
185
186         if (o->name == NULL)
187                 elog(E_FATAL, NULL, "no such option: [%s]", opt);
188
189         /* Call option setter first, so it can use both new and old values */
190         if (o->setter != NULL)
191                 o->setter(ctx, val);
192
193         /* Free old value if any */
194         if (ctx->options[o->index] != NULL)
195                 free(ctx->options[o->index]);
196         
197         /* Set new option value */
198         ctx->options[o->index] = val ? my_strdup(val) : NULL;
199 }
200
201 static void
202 show_cfg_page(struct shttpd_arg *arg)
203 {
204         struct shttpd_ctx       *ctx = arg->user_data;
205         char                    opt_name[20], value[BUFSIZ];
206         const struct opt        *o;
207
208         opt_name[0] = value[0] = '\0';
209
210         if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
211                 if (arg->flags & SHTTPD_MORE_POST_DATA)
212                         return;
213                 (void) shttpd_get_var("o", arg->in.buf, arg->in.len,
214                     opt_name, sizeof(opt_name));
215                 (void) shttpd_get_var("v", arg->in.buf, arg->in.len,
216                     value, sizeof(value));
217                 shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
218         }
219
220         shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
221             "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
222
223         shttpd_printf(arg, "%s", "<table border=1"
224             "<tr><th>Option</th><th>Description</th>"
225             "<th colspan=2>Value</th></tr>");
226
227         if (opt_name[0] != '\0' && value[0] != '\0')
228                 shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
229                     opt_name, value[0] ? value : "NULL");
230
231
232         for (o = known_options; o->name != NULL; o++) {
233                 shttpd_printf(arg,
234                     "<form method=post><tr><td>%s</td><td>%s</td>"
235                     "<input type=hidden name=o value='%s'>"
236                     "<td><input type=text name=v value='%s'></td>"
237                     "<td><input type=submit value=save></td></form></tr>",
238                     o->name, o->description, o->name,
239                     ctx->options[o->index] ? ctx->options[o->index] : "");
240         }
241
242         shttpd_printf(arg, "%s", "</table></body></html>");
243         arg->flags |= SHTTPD_END_OF_OUTPUT;
244 }
245 /*
246  * Show usage string and exit.
247  */
248 void
249 usage(const char *prog)
250 {
251         const struct opt        *o;
252
253         (void) fprintf(stderr,
254             "SHTTPD version %s (c) Sergey Lyubka\n"
255             "usage: %s [options] [config_file]\n", VERSION, prog);
256
257 #if !defined(NO_AUTH)
258         fprintf(stderr, "  -A <htpasswd_file> <realm> <user> <passwd>\n");
259 #endif /* NO_AUTH */
260
261         for (o = known_options; o->name != NULL; o++) {
262                 (void) fprintf(stderr, "  -%s\t%s", o->name, o->description);
263                 if (o->default_value != NULL)
264                         fprintf(stderr, " (default: %s)", o->default_value);
265                 fputc('\n', stderr);
266         }
267
268         exit(EXIT_FAILURE);
269 }
270
271 struct shttpd_ctx *shttpd_init(void)
272 {
273         struct shttpd_ctx       *ctx;
274         struct tm               *tm;
275         const struct opt        *o;
276
277         if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
278                 elog(E_FATAL, NULL, "cannot allocate shttpd context");
279
280         /* Set default values */
281         for (o = known_options; o->name != NULL; o++) {
282                 ctx->options[o->index] = o->default_value == NULL ?
283                     NULL : my_strdup(o->default_value);
284         }
285
286         current_time = ctx->start_time = time(NULL);
287         tm = localtime(&current_time);
288         tz_offset = 0;
289 #if 0
290         tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
291 #endif
292
293         InitializeCriticalSection(&ctx->mutex);
294
295         LL_INIT(&ctx->connections);
296         LL_INIT(&ctx->registered_uris);
297         LL_INIT(&ctx->error_handlers);
298         LL_INIT(&ctx->acl);
299         LL_INIT(&ctx->ssi_funcs);
300         LL_INIT(&ctx->listeners);
301
302 #ifdef _WIN32
303         {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
304 #endif /* _WIN32 */
305
306         return (ctx);
307 }