version 0.1.6
[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 /*
14  * Configuration parameters setters
15  */
16 static void
17 set_int(struct shttpd_ctx *ctx, void *ptr, const char *string)
18 {
19         ctx = NULL;     /* Unused */
20         * (int *) ptr = atoi(string);
21 }
22
23 static void
24 set_str(struct shttpd_ctx *ctx, void *ptr, const char *string)
25 {
26         ctx = NULL;     /* Unused */
27         * (char **) ptr = my_strdup(string);
28 }
29
30 static void
31 set_log_file(struct shttpd_ctx *ctx, void *ptr, const char *string)
32 {
33         FILE    **fp = ptr;
34         ctx = NULL;
35
36         if ((*fp = fopen(string, "a")) == NULL)
37                 elog(E_FATAL, NULL, "cannot open log file %s: %s",
38                     string, strerror(errno));
39 }
40
41 #ifndef NO_SSL
42 /*
43  * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
44  */
45 static void
46 set_ssl(struct shttpd_ctx *ctx, void *arg, const char *pem)
47 {
48         SSL_CTX         *CTX;
49         void            *lib;
50         struct ssl_func *fp;
51
52         arg = NULL;     /* Unused */
53
54         /* Load SSL library dynamically */
55         if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL)
56                 elog(E_FATAL, NULL, "set_ssl: cannot load %s", SSL_LIB);
57
58         for (fp = ssl_sw; fp->name != NULL; fp++)
59                 if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL)
60                         elog(E_FATAL, NULL,"set_ssl: cannot find %s", fp->name);
61
62         /* Initialize SSL crap */
63         SSL_library_init();
64
65         if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
66                 elog(E_FATAL, NULL, "SSL_CTX_new error");
67         else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
68                 elog(E_FATAL, NULL, "cannot open %s", pem);
69         else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
70                 elog(E_FATAL, NULL, "cannot open %s", pem);
71         ctx->ssl_ctx = CTX;
72 }
73 #endif /* NO_SSL */
74
75 static void
76 set_mime(struct shttpd_ctx *ctx, void *arg, const char *string)
77 {
78         arg = NULL;
79         set_mime_types(ctx, string);
80 }
81
82 #define OFS(x)  offsetof(struct shttpd_ctx, x)
83 #define BOOL_OPT        "0|1"
84 const struct opt options[] = {
85         {'d', "document_root", "Web root directory", set_str,
86                 OFS(document_root), "directory", NULL, OPT_DIR},
87         {'i', "index_files", "Index files", set_str, OFS(index_files),
88                 "file_list", INDEX_FILES, OPT_ADVANCED},
89         {'p', "listen_ports", "Listening ports", set_str,
90                 OFS(ports), "ports", LISTENING_PORTS, OPT_ADVANCED},
91         {'D', "list_directories", "Directory listing", set_int,
92                 OFS(dirlist), BOOL_OPT, "1", OPT_BOOL | OPT_ADVANCED},
93 #ifndef NO_CGI
94         {'c', "cgi_extensions", "CGI extensions", set_str,
95                 OFS(cgi_extensions), "ext_list", CGI_EXT, OPT_ADVANCED},
96         {'C', "cgi_interpreter", "CGI interpreter", set_str,
97                 OFS(cgi_interpreter), "file", NULL, OPT_FILE | OPT_ADVANCED},
98         {'V', "cgi_envvar", "CGI envir variables", set_str,
99                 OFS(cgi_vars), "X=Y,....", NULL, OPT_ADVANCED},
100 #endif /* NO_CGI */
101 #if !defined(NO_SSI)
102         {'S', "ssi_extensions", "SSI extensions", set_str,
103                 OFS(ssi_extensions), "ext_list", SSI_EXT, OPT_ADVANCED},
104 #endif /* NO_SSI */
105         {'N', "auth_realm", "Authentication realm", set_str,
106                 OFS(auth_realm), "auth_realm", REALM, OPT_ADVANCED},
107         {'l', "access_log", "Access log file", set_log_file,
108                 OFS(access_log), "file", NULL, OPT_FILE | OPT_ADVANCED},
109         {'e', "error_log", "Error log file", set_log_file,
110                 OFS(error_log), "file", NULL, OPT_FILE | OPT_ADVANCED},
111         {'m', "mime_types", "Mime types file", set_mime,
112                 OFS(mime_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
113         {'P', "global_htpasswd", "Global passwords file", set_str,
114                 OFS(global_passwd_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
115 #ifndef NO_SSL
116         {'s', "ssl_certificate", "SSL certificate file", set_ssl,
117                 OFS(ssl_ctx), "pem_file", NULL, OPT_FILE | OPT_ADVANCED},
118 #endif /* NO_SSL */
119         {'U', "put_auth", "PUT,DELETE auth file",set_str,
120                 OFS(put_auth_file), "file", NULL, OPT_FILE | OPT_ADVANCED},
121         {'a', "aliases", "Aliases", set_str,
122                 OFS(aliases), "X=Y,...", NULL, OPT_ADVANCED},
123         {'b', "io_buf_size", "IO buffer size", set_int, OFS(io_buf_size),
124                 "bytes", DFLT_IO_SIZ, OPT_INT | OPT_ADVANCED},
125 #ifdef _WIN32
126         {'B', "auto_start", "Autostart with Windows", set_int,
127                 OFS(auto_start), BOOL_OPT, "1", OPT_BOOL},
128 #else
129         {'I', "inetd_mode", "Inetd mode", set_int,
130                 OFS(inetd_mode), BOOL_OPT, NULL, OPT_BOOL       },
131         {'u', "runtime_uid", "Run as user", set_str,
132                 OFS(uid), "user_name", NULL, 0          },
133 #endif /* _WIN32 */
134         {0,   NULL, NULL, NULL, 0, NULL, NULL, 0        }
135 };
136
137 static const struct opt *
138 find_option(int sw, const char *name)
139 {
140         const struct opt        *opt;
141
142         for (opt = options; opt->sw != 0; opt++)
143                 if (sw == opt->sw || (name && strcmp(opt->name, name) == 0))
144                         return (opt);
145
146         return (NULL);
147 }
148
149 static void
150 set_option(const struct opt *opt, const char *val, char **tmpvars)
151 {
152         tmpvars += opt - options;
153
154         if (*tmpvars != NULL)
155                 free(*tmpvars);
156
157         *tmpvars = my_strdup(val);
158 }
159
160 /*
161  * Initialize shttpd context
162  */
163 static void
164 initialize_context(struct shttpd_ctx *ctx, const char *config_file,
165                 int argc, char *argv[], char **tmpvars)
166 {
167         char                    line[FILENAME_MAX], root[FILENAME_MAX],
168                                         var[sizeof(line)], val[sizeof(line)];
169         const char              *arg;
170         size_t                  i;
171         const struct opt        *opt;
172         FILE                    *fp;
173         struct tm               *tm;
174
175         current_time = time(NULL);
176         tm = localtime(&current_time);
177         tz_offset = 0;
178 #if 0
179         tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
180 #endif
181
182         (void) memset(ctx, 0, sizeof(*ctx));
183
184         ctx->start_time = current_time;
185         InitializeCriticalSection(&ctx->mutex);
186
187         LL_INIT(&ctx->connections);
188         LL_INIT(&ctx->mime_types);
189         LL_INIT(&ctx->registered_uris);
190         LL_INIT(&ctx->uri_auths);
191         LL_INIT(&ctx->error_handlers);
192
193 #if !defined(NO_SSI)
194         LL_INIT(&ctx->ssi_funcs);
195 #endif /* NO_SSI */
196
197         /* First pass: set the defaults */
198         for (opt = options; opt->sw != 0; opt++)
199                 if (tmpvars[opt - options] == NULL && opt->def != NULL)
200                         tmpvars[opt - options] = my_strdup(opt->def);
201
202         /* Second pass: load config file  */
203         if (config_file != NULL && (fp = fopen(config_file, "r")) != NULL) {
204                 DBG(("init_ctx: config file %s", config_file));
205
206                 /* Loop through the lines in config file */
207                 while (fgets(line, sizeof(line), fp) != NULL) {
208
209                         /* Skip comments and empty lines */
210                         if (line[0] == '#' || line[0] == '\n')
211                                 continue;
212
213                         /* Trim trailing newline character */
214                         line[strlen(line) - 1] = '\0';
215
216                         if (sscanf(line, "%s %[^#\n]", var, val) != 2)
217                                 elog(E_FATAL,0,"init_ctx: bad line: [%s]",line);
218
219                         if ((opt = find_option(0, var)) == NULL)
220                                 elog(E_FATAL, NULL, 
221                                     "set_option: unknown variable [%s]", var);
222                         set_option(opt, val, tmpvars);
223                 }
224                 (void) fclose(fp);
225         }
226
227         /* Third pass: process command line args */
228         for (i = 1; i < (size_t) argc && argv[i][0] == '-'; i++)
229                 if ((opt = find_option(argv[i][1], NULL)) != NULL) {
230                         arg = argv[i][2] ? &argv[i][2] : argv[++i];
231                         
232                         if (arg == NULL)
233                                 usage(argv[0]);
234
235                         set_option(opt, arg, tmpvars);
236                 } else {
237                         usage(argv[0]);
238                 }
239
240         /* Call setters functions now */
241         for (i = 0; i < NELEMS(options); i++)
242                 if (tmpvars[i] != NULL) {
243                         options[i].setter(ctx,
244                             ((char *) ctx) + options[i].ofs, tmpvars[i]);
245                         free(tmpvars[i]);
246                 }
247         
248         /* If document_root is not set, set it to current directory */
249         if (ctx->document_root == NULL) {
250                 (void) my_getcwd(root, sizeof(root));
251                 ctx->document_root = my_strdup(root);
252         }
253
254 #ifdef _WIN32
255         {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
256 #endif /* _WIN32 */
257
258         DBG(("init_ctx: initialized context %p", (void *) ctx));
259 }
260
261 /*
262  * Show usage string and exit.
263  */
264 void
265 usage(const char *prog)
266 {
267         const struct opt        *opt;
268
269         (void) fprintf(stderr,
270             "SHTTPD version %s (c) Sergey Lyubka\n"
271             "usage: %s [OPTIONS] [config_file]\n"
272             "Note: config line keyword for every option is in the "
273             "round brackets\n", VERSION, prog);
274
275 #if !defined(NO_AUTH)
276         (void) fprintf(stderr, "-A <htpasswd_file> <realm> <user> <passwd>\n");
277 #endif /* NO_AUTH */
278
279         for (opt = options; opt->name != NULL; opt++)
280                 (void) fprintf(stderr, "-%c <%s>\t\t%s (%s)\n",
281                     opt->sw, opt->arg, opt->desc, opt->name);
282
283         exit(EXIT_FAILURE);
284 }
285
286 struct shttpd_ctx *
287 init_from_argc_argv(const char *config_file, int argc, char *argv[])
288 {
289         struct shttpd_ctx       *ctx;
290         char                    *tmpvars[NELEMS(options)];
291         size_t                  i;
292
293         /* Initialize all temporary holders to NULL */
294         for (i = 0; i < NELEMS(tmpvars); i++)
295                 tmpvars[i] = NULL;
296
297         if ((ctx = malloc(sizeof(*ctx))) != NULL)
298                 initialize_context(ctx, config_file, argc, argv, tmpvars);
299         
300         return (ctx);
301 }
302
303 struct shttpd_ctx *
304 shttpd_init(const char *config_file, ...)
305 {
306         struct shttpd_ctx       *ctx;
307         va_list                 ap;
308         const char              *opt_name, *opt_value;
309         char                    *tmpvars[NELEMS(options)];
310         const struct opt        *opt;
311         size_t                  i;
312
313         /* Initialize all temporary holders to NULL */
314         for (i = 0; i < NELEMS(tmpvars); i++)
315                 tmpvars[i] = NULL;
316
317         if ((ctx = malloc(sizeof(*ctx))) != NULL) {
318
319                 va_start(ap, config_file);
320                 while ((opt_name = va_arg(ap, const char *)) != NULL) {
321                         opt_value = va_arg(ap, const char *);
322                         
323                         if ((opt = find_option(0, opt_name)) == NULL)
324                                 elog(E_FATAL, NULL, "shttpd_init: "
325                                     "unknown variable [%s]", opt_name);
326                         set_option(opt, opt_value, tmpvars);
327                 }
328                 va_end(ap);
329
330                 initialize_context(ctx, config_file, 0, NULL, tmpvars);
331         }
332
333         return (ctx);
334 }