source: trunk/third-party/shttpd/shttpd.c @ 6644

Last change on this file since 6644 was 6644, checked in by charles, 13 years ago

upgrade to shttpd 1.42

File size: 50.1 KB
Line 
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/*
12 * Small and portable HTTP server, http://shttpd.sourceforge.net
13 * $Id: shttpd.c,v 1.57 2008/08/23 21:00:38 drozd Exp $
14 */
15
16#include "defs.h"
17
18time_t  _shttpd_current_time;   /* Current UTC time             */
19int     _shttpd_tz_offset;      /* Time zone offset from UTC    */
20int     _shttpd_exit_flag;      /* Program exit flag            */
21
22const struct vec _shttpd_known_http_methods[] = {
23        {"GET",         3},
24        {"POST",        4},
25        {"PUT",         3},
26        {"DELETE",      6},
27        {"HEAD",        4},
28        {NULL,          0}
29};
30
31/*
32 * This structure tells how HTTP headers must be parsed.
33 * Used by parse_headers() function.
34 */
35#define OFFSET(x)       offsetof(struct headers, x)
36static const struct http_header http_headers[] = {
37        {16, HDR_INT,    OFFSET(cl),            "Content-Length: "      },
38        {14, HDR_STRING, OFFSET(ct),            "Content-Type: "        },
39        {12, HDR_STRING, OFFSET(useragent),     "User-Agent: "          },
40        {19, HDR_DATE,   OFFSET(ims),           "If-Modified-Since: "   },
41        {15, HDR_STRING, OFFSET(auth),          "Authorization: "       },
42        {9,  HDR_STRING, OFFSET(referer),       "Referer: "             },
43        {8,  HDR_STRING, OFFSET(cookie),        "Cookie: "              },
44        {10, HDR_STRING, OFFSET(location),      "Location: "            },
45        {8,  HDR_INT,    OFFSET(status),        "Status: "              },
46        {7,  HDR_STRING, OFFSET(range),         "Range: "               },
47        {12, HDR_STRING, OFFSET(connection),    "Connection: "          },
48        {19, HDR_STRING, OFFSET(transenc),      "Transfer-Encoding: "   },
49        {0,  HDR_INT,    0,                     NULL                    }
50};
51
52struct shttpd_ctx *init_ctx(const char *config_file, int argc, char *argv[]);
53static void process_connection(struct conn *, int, int);
54
55int
56_shttpd_is_true(const char *str)
57{
58        static const char       *trues[] = {"1", "yes", "true", "jawohl", NULL};
59        const char              **p;
60
61        for (p = trues; *p != NULL; p++)
62                if (str && !strcmp(str, *p))
63                        return (TRUE);
64
65        return (FALSE);
66}
67
68static void
69free_list(struct llhead *head, void (*dtor)(struct llhead *))
70{
71        struct llhead   *lp, *tmp;
72
73        LL_FOREACH_SAFE(head, lp, tmp) {
74                LL_DEL(lp);
75                dtor(lp);
76        }
77}
78
79static void
80listener_destructor(struct llhead *lp)
81{
82        struct listener *listener = LL_ENTRY(lp, struct listener, link);
83
84        (void) closesocket(listener->sock);
85        free(listener);
86}
87
88static void
89registered_uri_destructor(struct llhead *lp)
90{
91        struct registered_uri *ruri = LL_ENTRY(lp, struct registered_uri, link);
92
93        free((void *) ruri->uri);
94        free(ruri);
95}
96
97static void
98acl_destructor(struct llhead *lp)
99{
100        struct acl      *acl = LL_ENTRY(lp, struct acl, link);
101        free(acl);
102}
103
104int
105_shttpd_url_decode(const char *src, int src_len, char *dst, int dst_len)
106{
107        int     i, j, a, b;
108#define HEXTOI(x)  (isdigit(x) ? x - '0' : x - 'W')
109
110        for (i = j = 0; i < src_len && j < dst_len - 1; i++, j++)
111                switch (src[i]) {
112                case '%':
113                        if (isxdigit(((unsigned char *) src)[i + 1]) &&
114                            isxdigit(((unsigned char *) src)[i + 2])) {
115                                a = tolower(((unsigned char *)src)[i + 1]);
116                                b = tolower(((unsigned char *)src)[i + 2]);
117                                dst[j] = (HEXTOI(a) << 4) | HEXTOI(b);
118                                i += 2;
119                        } else {
120                                dst[j] = '%';
121                        }
122                        break;
123                default:
124                        dst[j] = src[i];
125                        break;
126                }
127
128        dst[j] = '\0';  /* Null-terminate the destination */
129
130        return (j);
131}
132
133static const char *
134is_alias(struct shttpd_ctx *ctx, const char *uri,
135                struct vec *a_uri, struct vec *a_path)
136{
137        const char      *p, *s = ctx->options[OPT_ALIASES];
138        size_t          len;
139
140        DBG(("is_alias: aliases [%s]", s == NULL ? "" : s));
141
142        FOR_EACH_WORD_IN_LIST(s, len) {
143
144                if ((p = memchr(s, '=', len)) == NULL || p >= s + len || p == s)
145                        continue;
146
147                if (memcmp(uri, s, p - s) == 0) {
148                        a_uri->ptr = s;
149                        a_uri->len = p - s;
150                        a_path->ptr = ++p;
151                        a_path->len = (s + len) - p;
152                        return (s);
153                }
154        }
155
156        return (NULL);
157}
158
159void
160_shttpd_stop_stream(struct stream *stream)
161{
162        if (stream->io_class != NULL && stream->io_class->close != NULL)
163                stream->io_class->close(stream);
164
165        stream->io_class= NULL;
166        stream->flags |= FLAG_CLOSED;
167        stream->flags &= ~(FLAG_R | FLAG_W | FLAG_ALWAYS_READY);
168
169        DBG(("%d %s stopped. %lu of content data, %d now in a buffer",
170            stream->conn->rem.chan.sock, 
171            stream->io_class ? stream->io_class->name : "(null)",
172            (unsigned long) stream->io.total, (int) io_data_len(&stream->io)));
173}
174
175/*
176 * Setup listening socket on given port, return socket
177 */
178static int
179shttpd_open_listening_port(int port)
180{
181        int             sock, on = 1;
182        struct usa      sa;
183
184#ifdef _WIN32
185        {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
186#endif /* _WIN32 */
187
188        sa.len                          = sizeof(sa.u.sin);
189        sa.u.sin.sin_family             = AF_INET;
190        sa.u.sin.sin_port               = htons((uint16_t) port);
191        sa.u.sin.sin_addr.s_addr        = htonl(INADDR_ANY);
192
193        if ((sock = socket(PF_INET, SOCK_STREAM, 6)) == -1)
194                goto fail;
195        if (_shttpd_set_non_blocking_mode(sock) != 0)
196                goto fail;
197        if (setsockopt(sock, SOL_SOCKET,
198            SO_REUSEADDR,(char *) &on, sizeof(on)) != 0)
199                goto fail;
200        if (bind(sock, &sa.u.sa, sa.len) < 0)
201                goto fail;
202        if (listen(sock, 128) != 0)
203                goto fail;
204
205#ifndef _WIN32
206        (void) fcntl(sock, F_SETFD, FD_CLOEXEC);
207#endif /* !_WIN32 */
208
209        return (sock);
210fail:
211        if (sock != -1)
212                (void) closesocket(sock);
213        _shttpd_elog(E_LOG, NULL, "open_listening_port(%d): %s", port, strerror(errno));
214        return (-1);
215}
216
217/*
218 * Check whether full request is buffered Return headers length, or 0
219 */
220int
221_shttpd_get_headers_len(const char *buf, size_t buflen)
222{
223        const char      *s, *e;
224        int             len = 0;
225
226        for (s = buf, e = s + buflen - 1; len == 0 && s < e; s++)
227                /* Control characters are not allowed but >=128 is. */
228                if (!isprint(* (unsigned char *) s) && *s != '\r' &&
229                    *s != '\n' && * (unsigned char *) s < 128)
230                        len = -1;
231                else if (s[0] == '\n' && s[1] == '\n')
232                        len = s - buf + 2;
233                else if (s[0] == '\n' && &s[1] < e &&
234                    s[1] == '\r' && s[2] == '\n')
235                        len = s - buf + 3;
236
237        return (len);
238}
239
240/*
241 * Send error message back to a client.
242 */
243void
244_shttpd_send_server_error(struct conn *c, int status, const char *reason)
245{
246        struct llhead           *lp;
247        struct error_handler    *e;
248
249        LL_FOREACH(&c->ctx->error_handlers, lp) {
250                e = LL_ENTRY(lp, struct error_handler, link);
251
252                if (e->code == status) {
253                        if (c->loc.io_class != NULL &&
254                            c->loc.io_class->close != NULL)
255                                c->loc.io_class->close(&c->loc);
256                        io_clear(&c->loc.io);
257                        _shttpd_setup_embedded_stream(c,
258                            e->callback, e->callback_data);
259                        return;
260                }
261        }
262
263        io_clear(&c->loc.io);
264        c->loc.io.head = _shttpd_snprintf(c->loc.io.buf, c->loc.io.size,
265            "HTTP/1.1 %d %s\r\n"
266            "Content-Type: text/plain\r\n"
267            "Content-Length: 12\r\n"
268            "\r\n"
269            "Error: %03d\r\n",
270            status, reason, status);
271        c->loc.content_len = 10;
272        c->status = status;
273        _shttpd_stop_stream(&c->loc);
274}
275
276/*
277 * Convert month to the month number. Return -1 on error, or month number
278 */
279static int
280montoi(const char *s)
281{
282        static const char *ar[] = {
283                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
284                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
285        };
286        size_t  i;
287
288        for (i = 0; i < sizeof(ar) / sizeof(ar[0]); i++)
289                if (!strcmp(s, ar[i]))
290                        return (i);
291
292        return (-1);
293}
294
295/*
296 * Parse date-time string, and return the corresponding time_t value
297 */
298static time_t
299date_to_epoch(const char *s)
300{
301        struct tm       tm, *tmp;
302        char            mon[32];
303        int             sec, min, hour, mday, month, year;
304
305        (void) memset(&tm, 0, sizeof(tm));
306        sec = min = hour = mday = month = year = 0;
307
308        if (((sscanf(s, "%d/%3s/%d %d:%d:%d",
309            &mday, mon, &year, &hour, &min, &sec) == 6) ||
310            (sscanf(s, "%d %3s %d %d:%d:%d",
311            &mday, mon, &year, &hour, &min, &sec) == 6) ||
312            (sscanf(s, "%*3s, %d %3s %d %d:%d:%d",
313            &mday, mon, &year, &hour, &min, &sec) == 6) ||
314            (sscanf(s, "%d-%3s-%d %d:%d:%d",
315            &mday, mon, &year, &hour, &min, &sec) == 6)) &&
316            (month = montoi(mon)) != -1) {
317                tm.tm_mday      = mday;
318                tm.tm_mon       = month;
319                tm.tm_year      = year;
320                tm.tm_hour      = hour;
321                tm.tm_min       = min;
322                tm.tm_sec       = sec;
323        }
324
325        if (tm.tm_year > 1900)
326                tm.tm_year -= 1900;
327        else if (tm.tm_year < 70)
328                tm.tm_year += 100;
329
330        /* Set Daylight Saving Time field */
331        tmp = localtime(&_shttpd_current_time);
332        tm.tm_isdst = tmp->tm_isdst;
333
334        return (mktime(&tm));
335}
336
337static void
338remove_double_dots(char *s)
339{
340        char    *p = s;
341
342        while (*s != '\0') {
343                *p++ = *s++;
344                if (s[-1] == '/' || s[-1] == '\\')
345                        while (*s == '.' || *s == '/' || *s == '\\')
346                                s++;
347        }
348        *p = '\0';
349}
350
351void
352_shttpd_parse_headers(const char *s, int len, struct headers *parsed)
353{
354        const struct http_header        *h;
355        union variant                   *v;
356        const char                      *p, *e = s + len;
357
358        DBG(("parsing headers (len %d): [%.*s]", len, len, s));
359
360        /* Loop through all headers in the request */
361        while (s < e) {
362
363                /* Find where this header ends */
364                for (p = s; p < e && *p != '\n'; ) p++;
365
366                /* Is this header known to us ? */
367                for (h = http_headers; h->len != 0; h++)
368                        if (e - s > h->len &&
369                            !_shttpd_strncasecmp(s, h->name, h->len))
370                                break;
371
372                /* If the header is known to us, store its value */
373                if (h->len != 0) {
374
375                        /* Shift to where value starts */
376                        s += h->len;
377
378                        /* Find place to store the value */
379                        v = (union variant *) ((char *) parsed + h->offset);
380
381                        /* Fetch header value into the connection structure */
382                        if (h->type == HDR_STRING) {
383                                v->v_vec.ptr = s;
384                                v->v_vec.len = p - s;
385                                if (p[-1] == '\r' && v->v_vec.len > 0)
386                                        v->v_vec.len--;
387                        } else if (h->type == HDR_INT) {
388                                v->v_big_int = strtoul(s, NULL, 10);
389                        } else if (h->type == HDR_DATE) {
390                                v->v_time = date_to_epoch(s);
391                        }
392                }
393
394                s = p + 1;      /* Shift to the next header */
395        }
396}
397
398static const struct {
399        const char      *extension;
400        int             ext_len;
401        const char      *mime_type;
402} builtin_mime_types[] = {
403        {"html",        4,      "text/html"                     },
404        {"htm",         3,      "text/html"                     },
405        {"txt",         3,      "text/plain"                    },
406        {"css",         3,      "text/css"                      },
407        {"ico",         3,      "image/x-icon"                  },
408        {"gif",         3,      "image/gif"                     },
409        {"jpg",         3,      "image/jpeg"                    },
410        {"jpeg",        4,      "image/jpeg"                    },
411        {"png",         3,      "image/png"                     },
412        {"svg",         3,      "image/svg+xml"                 },
413        {"torrent",     7,      "application/x-bittorrent"      },
414        {"wav",         3,      "audio/x-wav"                   },
415        {"mp3",         3,      "audio/x-mp3"                   },
416        {"mid",         3,      "audio/mid"                     },
417        {"m3u",         3,      "audio/x-mpegurl"               },
418        {"ram",         3,      "audio/x-pn-realaudio"          },
419        {"ra",          2,      "audio/x-pn-realaudio"          },
420        {"doc",         3,      "application/msword",           },
421        {"exe",         3,      "application/octet-stream"      },
422        {"zip",         3,      "application/x-zip-compressed"  },
423        {"xls",         3,      "application/excel"             },
424        {"tgz",         3,      "application/x-tar-gz"          },
425        {"tar.gz",      6,      "application/x-tar-gz"          },
426        {"tar",         3,      "application/x-tar"             },
427        {"gz",          2,      "application/x-gunzip"          },
428        {"arj",         3,      "application/x-arj-compressed"  },
429        {"rar",         3,      "application/x-arj-compressed"  },
430        {"rtf",         3,      "application/rtf"               },
431        {"pdf",         3,      "application/pdf"               },
432        {"swf",         3,      "application/x-shockwave-flash" },
433        {"mpg",         3,      "video/mpeg"                    },
434        {"mpeg",        4,      "video/mpeg"                    },
435        {"asf",         3,      "video/x-ms-asf"                },
436        {"avi",         3,      "video/x-msvideo"               },
437        {"bmp",         3,      "image/bmp"                     },
438        {NULL,          0,      NULL                            }
439};
440
441void
442_shttpd_get_mime_type(struct shttpd_ctx *ctx,
443                const char *uri, int len, struct vec *vec)
444{
445        const char      *eq, *p = ctx->options[OPT_MIME_TYPES];
446        int             i, n, ext_len;
447
448        /* Firt, loop through the custom mime types if any */
449        FOR_EACH_WORD_IN_LIST(p, n) {
450                if ((eq = memchr(p, '=', n)) == NULL || eq >= p + n || eq == p)
451                        continue;
452                ext_len = eq - p;
453                if (len > ext_len && uri[len - ext_len - 1] == '.' &&
454                    !_shttpd_strncasecmp(p, &uri[len - ext_len], ext_len)) {
455                        vec->ptr = eq + 1;
456                        vec->len = p + n - vec->ptr;
457                        return;
458                }
459        }
460
461        /* If no luck, try built-in mime types */
462        for (i = 0; builtin_mime_types[i].extension != NULL; i++) {
463                ext_len = builtin_mime_types[i].ext_len;
464                if (len > ext_len && uri[len - ext_len - 1] == '.' &&
465                    !_shttpd_strncasecmp(builtin_mime_types[i].extension,
466                            &uri[len - ext_len], ext_len)) {
467                        vec->ptr = builtin_mime_types[i].mime_type;
468                        vec->len = strlen(vec->ptr);
469                        return;
470                }
471        }
472
473        /* Oops. This extension is unknown to us. Fallback to text/plain */
474        vec->ptr = "text/plain";
475        vec->len = strlen(vec->ptr);
476}
477
478/*
479 * For given directory path, substitute it to valid index file.
480 * Return 0 if index file has been found, -1 if not found
481 */
482static int
483find_index_file(struct conn *c, char *path, size_t maxpath, struct stat *stp)
484{
485        char            buf[FILENAME_MAX];
486        const char      *s = c->ctx->options[OPT_INDEX_FILES];
487        size_t          len;
488
489        FOR_EACH_WORD_IN_LIST(s, len) {
490                /* path must end with '/' character */
491                _shttpd_snprintf(buf, sizeof(buf), "%s%.*s", path, len, s);
492                if (_shttpd_stat(buf, stp) == 0) {
493                        _shttpd_strlcpy(path, buf, maxpath);
494                        _shttpd_get_mime_type(c->ctx, s, len, &c->mime_type);
495                        return (0);
496                }
497        }
498
499        return (-1);
500}
501
502/*
503 * Try to open requested file, return 0 if OK, -1 if error.
504 * If the file is given arguments using PATH_INFO mechanism,
505 * initialize pathinfo pointer.
506 */
507static int
508get_path_info(struct conn *c, char *path, struct stat *stp)
509{
510        char    *p, *e;
511
512        if (_shttpd_stat(path, stp) == 0)
513                return (0);
514
515        p = path + strlen(path);
516        e = path + strlen(c->ctx->options[OPT_ROOT]) + 2;
517       
518        /* Strip directory parts of the path one by one */
519        for (; p > e; p--)
520                if (*p == '/') {
521                        *p = '\0';
522                        if (!_shttpd_stat(path, stp) && !S_ISDIR(stp->st_mode)) {
523                                c->path_info = p + 1;
524                                return (0);
525                        } else {
526                                *p = '/';
527                        }
528                }
529
530        return (-1);
531}
532
533static void
534decide_what_to_do(struct conn *c)
535{
536        char            path[URI_MAX], buf[1024], *root;
537        struct vec      alias_uri, alias_path;
538        struct stat     st;
539        int             rc;
540        struct registered_uri   *ruri;
541
542        DBG(("decide_what_to_do: [%s]", c->uri));
543
544        if ((c->query = strchr(c->uri, '?')) != NULL)
545                *c->query++ = '\0';
546
547        _shttpd_url_decode(c->uri, strlen(c->uri), c->uri, strlen(c->uri) + 1);
548        remove_double_dots(c->uri);
549       
550        root = c->ctx->options[OPT_ROOT];
551        if (strlen(c->uri) + strlen(root) >= sizeof(path)) {
552                _shttpd_send_server_error(c, 400, "URI is too long");
553                return;
554        }
555
556        (void) _shttpd_snprintf(path, sizeof(path), "%s%s", root, c->uri);
557
558        /* User may use the aliases - check URI for mount point */
559        if (is_alias(c->ctx, c->uri, &alias_uri, &alias_path) != NULL) {
560                (void) _shttpd_snprintf(path, sizeof(path), "%.*s%s",
561                    alias_path.len, alias_path.ptr, c->uri + alias_uri.len);
562                DBG(("using alias %.*s -> %.*s", alias_uri.len, alias_uri.ptr,
563                    alias_path.len, alias_path.ptr));
564        }
565
566#if !defined(NO_AUTH)
567        if (_shttpd_check_authorization(c, path) != 1) {
568                _shttpd_send_authorization_request(c);
569        } else
570#endif /* NO_AUTH */
571        if ((ruri = _shttpd_is_registered_uri(c->ctx, c->uri)) != NULL) {
572                _shttpd_setup_embedded_stream(c,
573                    ruri->callback, ruri->callback_data);
574        } else
575        if (strstr(path, HTPASSWD)) {
576                /* Do not allow to view passwords files */
577                _shttpd_send_server_error(c, 403, "Forbidden");
578        } else
579#if !defined(NO_AUTH)
580        if ((c->method == METHOD_PUT || c->method == METHOD_DELETE) &&
581            (c->ctx->options[OPT_AUTH_PUT] == NULL ||
582             !_shttpd_is_authorized_for_put(c))) {
583                _shttpd_send_authorization_request(c);
584        } else
585#endif /* NO_AUTH */
586        if (c->method == METHOD_PUT) {
587                c->status = _shttpd_stat(path, &st) == 0 ? 200 : 201;
588
589                if (c->ch.range.v_vec.len > 0) {
590                        _shttpd_send_server_error(c, 501,
591                            "PUT Range Not Implemented");
592                } else if ((rc = _shttpd_put_dir(path)) == 0) {
593                        _shttpd_send_server_error(c, 200, "OK");
594                } else if (rc == -1) {
595                        _shttpd_send_server_error(c, 500, "PUT Directory Error");
596                } else if (c->rem.content_len == 0) {
597                        _shttpd_send_server_error(c, 411, "Length Required");
598                } else if ((c->loc.chan.fd = _shttpd_open(path, O_WRONLY | O_BINARY |
599                    O_CREAT | O_NONBLOCK | O_TRUNC, 0644)) == -1) {
600                        _shttpd_send_server_error(c, 500, "PUT Error");
601                } else {
602                        DBG(("PUT file [%s]", c->uri));
603                        c->loc.io_class = &_shttpd_io_file;
604                        c->loc.flags |= FLAG_W | FLAG_ALWAYS_READY ;
605                }
606        } else if (c->method == METHOD_DELETE) {
607                DBG(("DELETE [%s]", c->uri));
608                if (_shttpd_remove(path) == 0)
609                        _shttpd_send_server_error(c, 200, "OK");
610                else
611                        _shttpd_send_server_error(c, 500, "DELETE Error");
612        } else if (get_path_info(c, path, &st) != 0) {
613                _shttpd_send_server_error(c, 404, "Not Found");
614        } else if (S_ISDIR(st.st_mode) && path[strlen(path) - 1] != '/') {
615                (void) _shttpd_snprintf(buf, sizeof(buf),
616                        "Moved Permanently\r\nLocation: %s/", c->uri);
617                _shttpd_send_server_error(c, 301, buf);
618        } else if (S_ISDIR(st.st_mode) &&
619            find_index_file(c, path, sizeof(path) - 1, &st) == -1 &&
620            !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
621                _shttpd_send_server_error(c, 403, "Directory Listing Denied");
622        } else if (S_ISDIR(st.st_mode) && IS_TRUE(c->ctx, OPT_DIR_LIST)) {
623                if ((c->loc.chan.dir.path = _shttpd_strdup(path)) != NULL)
624                        _shttpd_get_dir(c);
625                else
626                        _shttpd_send_server_error(c, 500, "GET Directory Error");
627        } else if (S_ISDIR(st.st_mode) && !IS_TRUE(c->ctx, OPT_DIR_LIST)) {
628                _shttpd_send_server_error(c, 403, "Directory listing denied");
629#if !defined(NO_CGI)
630        } else if (_shttpd_match_extension(path,
631            c->ctx->options[OPT_CGI_EXTENSIONS])) {
632                if (c->method != METHOD_POST && c->method != METHOD_GET) {
633                        _shttpd_send_server_error(c, 501, "Bad method ");
634                } else if ((_shttpd_run_cgi(c, path)) == -1) {
635                        _shttpd_send_server_error(c, 500, "Cannot exec CGI");
636                } else {
637                        _shttpd_do_cgi(c);
638                }
639#endif /* NO_CGI */
640#if !defined(NO_SSI)
641        } else if (_shttpd_match_extension(path,
642            c->ctx->options[OPT_SSI_EXTENSIONS])) {
643                if ((c->loc.chan.fd = _shttpd_open(path,
644                    O_RDONLY | O_BINARY, 0644)) == -1) {
645                        _shttpd_send_server_error(c, 500, "SSI open error");
646                } else {
647                        _shttpd_do_ssi(c);
648                }
649#endif /* NO_CGI */
650        } else if (c->ch.ims.v_time && st.st_mtime <= c->ch.ims.v_time) {
651                _shttpd_send_server_error(c, 304, "Not Modified");
652        } else if ((c->loc.chan.fd = _shttpd_open(path,
653            O_RDONLY | O_BINARY, 0644)) != -1) {
654                _shttpd_get_file(c, &st);
655        } else {
656                _shttpd_send_server_error(c, 500, "Internal Error");
657        }
658}
659
660static int
661set_request_method(struct conn *c)
662{
663        const struct vec        *v;
664
665        /* Set the request method */
666        for (v = _shttpd_known_http_methods; v->ptr != NULL; v++)
667                if (!memcmp(c->rem.io.buf, v->ptr, v->len)) {
668                        c->method = v - _shttpd_known_http_methods;
669                        break;
670                }
671
672        return (v->ptr == NULL);
673}
674
675static void
676parse_http_request(struct conn *c)
677{
678        char    *s, *e, *p, *start;
679        int     uri_len, req_len, n;
680
681        s = io_data(&c->rem.io);;
682        req_len = c->rem.headers_len =
683            _shttpd_get_headers_len(s, io_data_len(&c->rem.io));
684
685        if (req_len == 0 && io_space_len(&c->rem.io) == 0) {
686                io_clear(&c->rem.io);
687                _shttpd_send_server_error(c, 400, "Request is too big");
688        }
689
690        if (req_len == 0) {
691                return;
692        } else if (req_len < 16) {      /* Minimal: "GET / HTTP/1.0\n\n" */
693                _shttpd_send_server_error(c, 400, "Bad request");
694        } else if (set_request_method(c)) {
695                _shttpd_send_server_error(c, 501, "Method Not Implemented");
696        } else if ((c->request = _shttpd_strndup(s, req_len)) == NULL) {
697                _shttpd_send_server_error(c, 500, "Cannot allocate request");
698        }
699
700        if (c->loc.flags & FLAG_CLOSED)
701                return;
702
703        io_inc_tail(&c->rem.io, req_len);
704
705        DBG(("Conn %d: parsing request: [%.*s]", c->rem.chan.sock, req_len, s));
706        c->rem.flags |= FLAG_HEADERS_PARSED;
707
708        /* Set headers pointer. Headers follow the request line */
709        c->headers = memchr(c->request, '\n', req_len);
710        assert(c->headers != NULL);
711        assert(c->headers < c->request + req_len);
712        if (c->headers > c->request && c->headers[-1] == '\r')
713                c->headers[-1] = '\0';
714        *c->headers++ = '\0';
715
716        /*
717         * Now make a copy of the URI, because it will be URL-decoded,
718         * and we need a copy of unmodified URI for the access log.
719         * First, we skip the REQUEST_METHOD and shift to the URI.
720         */
721        for (p = c->request, e = p + req_len; *p != ' ' && p < e; p++);
722        while (p < e && *p == ' ')
723                p++;
724
725        /* Now remember where URI starts, and shift to the end of URI */
726        for (start = p; p < e && !isspace((unsigned char)*p); ) p++;
727        uri_len = p - start;
728
729        /* Skip space following the URI */
730        while (p < e && *p == ' ')
731                p++;
732
733        /* Now comes the HTTP-Version in the form HTTP/<major>.<minor> */
734        if (sscanf(p, "HTTP/%lu.%lu%n",
735            &c->major_version, &c->minor_version, &n) != 2 || p[n] != '\0') {
736                _shttpd_send_server_error(c, 400, "Bad HTTP version");
737        } else if (c->major_version > 1 ||
738            (c->major_version == 1 && c->minor_version > 1)) {
739                _shttpd_send_server_error(c, 505, "HTTP version not supported");
740        } else if (uri_len <= 0) {
741                _shttpd_send_server_error(c, 400, "Bad URI");
742        } else if ((c->uri = malloc(uri_len + 1)) == NULL) {
743                _shttpd_send_server_error(c, 500, "Cannot allocate URI");
744        } else {
745                _shttpd_strlcpy(c->uri, (char *) start, uri_len + 1);
746                _shttpd_parse_headers(c->headers,
747                    (c->request + req_len) - c->headers, &c->ch);
748
749                /* Remove the length of request from total, count only data */
750                assert(c->rem.io.total >= (big_int_t) req_len);
751                c->rem.io.total -= req_len;
752                c->rem.content_len = c->ch.cl.v_big_int;
753                decide_what_to_do(c);
754        }
755}
756
757static void
758add_socket(struct worker *worker, int sock, int is_ssl)
759{
760        struct shttpd_ctx       *ctx = worker->ctx;
761        struct conn             *c;
762        struct usa              sa;
763        int                     l = IS_TRUE(ctx, OPT_INETD) ? E_FATAL : E_LOG;
764#if !defined(NO_SSL)
765        SSL             *ssl = NULL;
766#else
767        is_ssl = is_ssl;        /* supress warnings */
768#endif /* NO_SSL */
769
770        sa.len = sizeof(sa.u.sin);
771        (void) _shttpd_set_non_blocking_mode(sock);
772
773        if (getpeername(sock, &sa.u.sa, &sa.len)) {
774                _shttpd_elog(l, NULL, "add_socket: %s", strerror(errno));
775#if !defined(NO_SSL)
776        } else if (is_ssl && (ssl = SSL_new(ctx->ssl_ctx)) == NULL) {
777                _shttpd_elog(l, NULL, "add_socket: SSL_new: %s", strerror(ERRNO));
778                (void) closesocket(sock);
779        } else if (is_ssl && SSL_set_fd(ssl, sock) == 0) {
780                _shttpd_elog(l, NULL, "add_socket: SSL_set_fd: %s", strerror(ERRNO));
781                (void) closesocket(sock);
782                SSL_free(ssl);
783#endif /* NO_SSL */
784        } else if ((c = calloc(1, sizeof(*c) + 2 * URI_MAX)) == NULL) {
785#if !defined(NO_SSL)
786                if (ssl)
787                        SSL_free(ssl);
788#endif /* NO_SSL */
789                (void) closesocket(sock);
790                _shttpd_elog(l, NULL, "add_socket: calloc: %s", strerror(ERRNO));
791        } else {
792                c->rem.conn     = c->loc.conn = c;
793                c->ctx          = ctx;
794                c->worker       = worker;
795                c->sa           = sa;
796                c->birth_time   = _shttpd_current_time;
797                c->expire_time  = _shttpd_current_time + EXPIRE_TIME;
798
799                (void) getsockname(sock, &sa.u.sa, &sa.len);
800                c->loc_port = sa.u.sin.sin_port;
801
802                _shttpd_set_close_on_exec(sock);
803
804                c->loc.io_class = NULL;
805       
806                c->rem.io_class = &_shttpd_io_socket;
807                c->rem.chan.sock = sock;
808
809                /* Set IO buffers */
810                c->loc.io.buf   = (char *) (c + 1);
811                c->rem.io.buf   = c->loc.io.buf + URI_MAX;
812                c->loc.io.size  = c->rem.io.size = URI_MAX;
813
814#if !defined(NO_SSL)
815                if (is_ssl) {
816                        c->rem.io_class = &_shttpd_io_ssl;
817                        c->rem.chan.ssl.sock = sock;
818                        c->rem.chan.ssl.ssl = ssl;
819                        _shttpd_ssl_handshake(&c->rem);
820                }
821#endif /* NO_SSL */
822
823                LL_TAIL(&worker->connections, &c->link);
824                worker->num_conns++;
825               
826                DBG(("%s:%hu connected (socket %d)",
827                    inet_ntoa(* (struct in_addr *) &sa.u.sin.sin_addr.s_addr),
828                    ntohs(sa.u.sin.sin_port), sock));
829        }
830}
831
832static struct worker *
833first_worker(struct shttpd_ctx *ctx)
834{
835        return (LL_ENTRY(ctx->workers.next, struct worker, link));
836}
837
838static void
839pass_socket(struct shttpd_ctx *ctx, int sock, int is_ssl)
840{
841        struct llhead   *lp;
842        struct worker   *worker, *lazy;
843        int             buf[3];
844
845        lazy = first_worker(ctx);
846
847        /* Find least busy worker */
848        LL_FOREACH(&ctx->workers, lp) {
849                worker = LL_ENTRY(lp, struct worker, link);
850                if (worker->num_conns < lazy->num_conns)
851                        lazy = worker;
852        }
853
854        buf[0] = CTL_PASS_SOCKET;
855        buf[1] = sock;
856        buf[2] = is_ssl;
857
858        (void) send(lazy->ctl[1], (void *) buf, sizeof(buf), 0);
859}
860
861static int
862set_ports(struct shttpd_ctx *ctx, const char *p)
863{
864        int             sock, len, is_ssl, port;
865        struct listener *l;
866
867
868        free_list(&ctx->listeners, &listener_destructor);
869
870        FOR_EACH_WORD_IN_LIST(p, len) {
871
872                is_ssl  = p[len - 1] == 's' ? 1 : 0;
873                port    = atoi(p);
874
875                if ((sock = shttpd_open_listening_port(port)) == -1) {
876                        _shttpd_elog(E_LOG, NULL, "cannot open port %d", port);
877                        goto fail;
878                } else if (is_ssl && ctx->ssl_ctx == NULL) {
879                        (void) closesocket(sock);
880                        _shttpd_elog(E_LOG, NULL, "cannot add SSL socket, "
881                            "please specify certificate file");
882                        goto fail;
883                } else if ((l = calloc(1, sizeof(*l))) == NULL) {
884                        (void) closesocket(sock);
885                        _shttpd_elog(E_LOG, NULL, "cannot allocate listener");
886                        goto fail;
887                } else {
888                        l->is_ssl = is_ssl;
889                        l->sock = sock;
890                        l->ctx  = ctx;
891                        LL_TAIL(&ctx->listeners, &l->link);
892                        DBG(("shttpd_listen: added socket %d", sock));
893                }
894        }
895
896        return (TRUE);
897fail:
898        free_list(&ctx->listeners, &listener_destructor);
899        return (FALSE);
900}
901
902static void
903read_stream(struct stream *stream)
904{
905        int     n, len;
906
907        len = io_space_len(&stream->io);
908        assert(len > 0);
909
910        /* Do not read more that needed */
911        if (stream->content_len > 0 &&
912            stream->io.total + len > stream->content_len)
913                len = stream->content_len - stream->io.total;
914
915        /* Read from underlying channel */
916        assert(stream->io_class != NULL);
917        n = stream->io_class->read(stream, io_space(&stream->io), len);
918
919        if (n > 0)
920                io_inc_head(&stream->io, n);
921        else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
922                n = n;  /* Ignore EINTR and EAGAIN */
923        else if (!(stream->flags & FLAG_DONT_CLOSE))
924                _shttpd_stop_stream(stream);
925
926        DBG(("read_stream (%d %s): read %d/%d/%lu bytes (errno %d)",
927            stream->conn->rem.chan.sock,
928            stream->io_class ? stream->io_class->name : "(null)",
929            n, len, (unsigned long) stream->io.total, ERRNO));
930
931        /*
932         * Close the local stream if everything was read
933         * XXX We do not close the remote stream though! It may be
934         * a POST data completed transfer, we do not want the socket
935         * to be closed.
936         */
937        if (stream->content_len > 0 && stream == &stream->conn->loc) {
938                assert(stream->io.total <= stream->content_len);
939                if (stream->io.total == stream->content_len)
940                        _shttpd_stop_stream(stream);
941        }
942
943        stream->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
944}
945
946static void
947write_stream(struct stream *from, struct stream *to)
948{
949        int     n, len;
950
951        len = io_data_len(&from->io);
952        assert(len > 0);
953
954        /* TODO: should be assert on CAN_WRITE flag */
955        n = to->io_class->write(to, io_data(&from->io), len);
956        to->conn->expire_time = _shttpd_current_time + EXPIRE_TIME;
957        DBG(("write_stream (%d %s): written %d/%d bytes (errno %d)",
958            to->conn->rem.chan.sock,
959            to->io_class ? to->io_class->name : "(null)", n, len, ERRNO));
960
961        if (n > 0)
962                io_inc_tail(&from->io, n);
963        else if (n == -1 && (ERRNO == EINTR || ERRNO == EWOULDBLOCK))
964                n = n;  /* Ignore EINTR and EAGAIN */
965        else if (!(to->flags & FLAG_DONT_CLOSE))
966                _shttpd_stop_stream(to);
967}
968
969
970static void
971connection_desctructor(struct llhead *lp)
972{
973        struct conn             *c = LL_ENTRY(lp, struct conn, link);
974        static const struct vec vec = {"close", 5};
975        int                     do_close;
976
977        DBG(("Disconnecting %d (%.*s)", c->rem.chan.sock,
978            c->ch.connection.v_vec.len, c->ch.connection.v_vec.ptr));
979
980        if (c->request != NULL && c->ctx->access_log != NULL)
981                _shttpd_log_access(c->ctx->access_log, c);
982
983        /* In inetd mode, exit if request is finished. */
984        if (IS_TRUE(c->ctx, OPT_INETD))
985                exit(0);
986
987        if (c->loc.io_class != NULL && c->loc.io_class->close != NULL)
988                c->loc.io_class->close(&c->loc);
989
990        /*
991         * Check the "Connection: " header before we free c->request
992         * If it its 'keep-alive', then do not close the connection
993         */
994        do_close = (c->ch.connection.v_vec.len >= vec.len &&
995            !_shttpd_strncasecmp(vec.ptr,c->ch.connection.v_vec.ptr,vec.len)) ||
996            (c->major_version < 1 ||
997            (c->major_version >= 1 && c->minor_version < 1));
998
999        if (c->request)
1000                free(c->request);
1001        if (c->uri)
1002                free(c->uri);
1003
1004        /* Keep the connection open only if we have Content-Length set */
1005        if (!do_close && c->loc.content_len > 0) {
1006                c->loc.io_class = NULL;
1007                c->loc.flags = 0;
1008                c->loc.content_len = 0;
1009                c->rem.flags = FLAG_W | FLAG_R | FLAG_SSL_ACCEPTED;
1010                c->query = c->request = c->uri = c->path_info = NULL;
1011                c->mime_type.len = 0;
1012                (void) memset(&c->ch, 0, sizeof(c->ch));
1013                io_clear(&c->loc.io);
1014                c->birth_time = _shttpd_current_time;
1015                if (io_data_len(&c->rem.io) > 0)
1016                        process_connection(c, 0, 0);
1017        } else {
1018                if (c->rem.io_class != NULL)
1019                        c->rem.io_class->close(&c->rem);
1020
1021                LL_DEL(&c->link);
1022                c->worker->num_conns--;
1023                assert(c->worker->num_conns >= 0);
1024
1025                free(c);
1026        }
1027}
1028
1029static void
1030worker_destructor(struct llhead *lp)
1031{
1032        struct worker   *worker = LL_ENTRY(lp, struct worker, link);
1033
1034        free_list(&worker->connections, connection_desctructor);
1035        free(worker);
1036}
1037
1038static int
1039is_allowed(const struct shttpd_ctx *ctx, const struct usa *usa)
1040{
1041        const struct acl        *acl;
1042        const struct llhead     *lp;
1043        int                     allowed = '+';
1044        uint32_t                ip;
1045
1046        LL_FOREACH(&ctx->acl, lp) {
1047                acl = LL_ENTRY(lp, struct acl, link);
1048                (void) memcpy(&ip, &usa->u.sin.sin_addr, sizeof(ip));
1049                if (acl->ip == (ntohl(ip) & acl->mask))
1050                        allowed = acl->flag;
1051        }
1052
1053        return (allowed == '+');
1054}
1055
1056static void
1057add_to_set(int fd, fd_set *set, int *max_fd)
1058{
1059        FD_SET(fd, set);
1060        if (fd > *max_fd)
1061                *max_fd = fd;
1062}
1063
1064static void
1065process_connection(struct conn *c, int remote_ready, int local_ready)
1066{
1067        /* Read from remote end if it is ready */
1068        if (remote_ready && io_space_len(&c->rem.io))
1069                read_stream(&c->rem);
1070
1071        /* If the request is not parsed yet, do so */
1072        if (!(c->rem.flags & FLAG_HEADERS_PARSED))
1073                parse_http_request(c);
1074
1075        DBG(("loc: %d [%.*s]", (int) io_data_len(&c->loc.io),
1076            (int) io_data_len(&c->loc.io), io_data(&c->loc.io)));
1077        DBG(("rem: %d [%.*s]", (int) io_data_len(&c->rem.io),
1078            (int) io_data_len(&c->rem.io), io_data(&c->rem.io)));
1079
1080        /* Read from the local end if it is ready */
1081        if (local_ready && io_space_len(&c->loc.io))
1082                read_stream(&c->loc);
1083
1084        if (io_data_len(&c->rem.io) > 0 && (c->loc.flags & FLAG_W) &&
1085            c->loc.io_class != NULL && c->loc.io_class->write != NULL)
1086                write_stream(&c->rem, &c->loc);
1087
1088        if (io_data_len(&c->loc.io) > 0 && c->rem.io_class != NULL)
1089                write_stream(&c->loc, &c->rem); 
1090
1091        /* Check whether we should close this connection */
1092        if ((_shttpd_current_time > c->expire_time) ||
1093            (c->rem.flags & FLAG_CLOSED) ||
1094            ((c->loc.flags & FLAG_CLOSED) && !io_data_len(&c->loc.io)))
1095                connection_desctructor(&c->link);
1096}
1097
1098static int
1099num_workers(const struct shttpd_ctx *ctx)
1100{
1101        char    *p = ctx->options[OPT_THREADS];
1102        return (p ? atoi(p) : 1);
1103}
1104
1105static void
1106handle_connected_socket(struct shttpd_ctx *ctx,
1107                struct usa *sap, int sock, int is_ssl)
1108{
1109#if !defined(_WIN32)
1110        if (sock >= (int) FD_SETSIZE) {
1111                _shttpd_elog(E_LOG, NULL, "ctx %p: discarding "
1112                    "socket %d, too busy", ctx, sock);
1113                (void) closesocket(sock);
1114        } else
1115#endif /* !_WIN32 */
1116                if (!is_allowed(ctx, sap)) {
1117                _shttpd_elog(E_LOG, NULL, "%s is not allowed to connect",
1118                    inet_ntoa(sap->u.sin.sin_addr));
1119                (void) closesocket(sock);
1120        } else if (num_workers(ctx) > 1) {
1121                pass_socket(ctx, sock, is_ssl);
1122        } else {
1123                add_socket(first_worker(ctx), sock, is_ssl);
1124        }
1125}
1126
1127static int
1128do_select(int max_fd, fd_set *read_set, fd_set *write_set, int milliseconds)
1129{
1130        struct timeval  tv;
1131        int             n;
1132
1133        tv.tv_sec = milliseconds / 1000;
1134        tv.tv_usec = (milliseconds % 1000) * 1000;
1135
1136        /* Check IO readiness */
1137        if ((n = select(max_fd + 1, read_set, write_set, NULL, &tv)) < 0) {
1138#ifdef _WIN32
1139                /*
1140                 * On windows, if read_set and write_set are empty,
1141                 * select() returns "Invalid parameter" error
1142                 * (at least on my Windows XP Pro). So in this case,
1143                 * we sleep here.
1144                 */
1145                Sleep(milliseconds);
1146#endif /* _WIN32 */
1147                DBG(("select: %d", ERRNO));
1148        }
1149
1150        return (n);
1151}
1152
1153static int
1154multiplex_worker_sockets(const struct worker *worker, int *max_fd,
1155                fd_set *read_set, fd_set *write_set)
1156{
1157        struct llhead   *lp;
1158        struct conn     *c;
1159        int             nowait = FALSE;
1160
1161        /* Add control socket */
1162        add_to_set(worker->ctl[0], read_set, max_fd);
1163
1164        /* Multiplex streams */
1165        LL_FOREACH(&worker->connections, lp) {
1166                c = LL_ENTRY(lp, struct conn, link);
1167               
1168                /* If there is a space in remote IO, check remote socket */
1169                if (io_space_len(&c->rem.io))
1170                        add_to_set(c->rem.chan.fd, read_set, max_fd);
1171
1172#if !defined(NO_CGI)
1173                /*
1174                 * If there is a space in local IO, and local endpoint is
1175                 * CGI, check local socket for read availability
1176                 */
1177                if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
1178                    c->loc.io_class == &_shttpd_io_cgi)
1179                        add_to_set(c->loc.chan.fd, read_set, max_fd);
1180
1181                /*
1182                 * If there is some data read from remote socket, and
1183                 * local endpoint is CGI, check local for write availability
1184                 */
1185                if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
1186                    c->loc.io_class == &_shttpd_io_cgi)
1187                        add_to_set(c->loc.chan.fd, write_set, max_fd);
1188#endif /* NO_CGI */
1189
1190                /*
1191                 * If there is some data read from local endpoint, check the
1192                 * remote socket for write availability
1193                 */
1194                if (io_data_len(&c->loc.io) && !(c->loc.flags & FLAG_SUSPEND))
1195                        add_to_set(c->rem.chan.fd, write_set, max_fd);
1196
1197                /*
1198                 * Set select wait interval to zero if FLAG_ALWAYS_READY set
1199                 */
1200                if (io_space_len(&c->loc.io) && (c->loc.flags & FLAG_R) &&
1201                    (c->loc.flags & FLAG_ALWAYS_READY))
1202                        nowait = TRUE;
1203               
1204                if (io_data_len(&c->rem.io) && (c->loc.flags & FLAG_W) &&
1205                    (c->loc.flags & FLAG_ALWAYS_READY))
1206                        nowait = TRUE;
1207        }
1208
1209        return (nowait);
1210}
1211
1212int
1213shttpd_join(struct shttpd_ctx *ctx,
1214                fd_set *read_set, fd_set *write_set, int *max_fd)
1215{
1216        struct llhead   *lp;
1217        struct listener *l;
1218        int             nowait = FALSE;
1219
1220        /* Add listening sockets to the read set */
1221        LL_FOREACH(&ctx->listeners, lp) {
1222                l = LL_ENTRY(lp, struct listener, link);
1223                add_to_set(l->sock, read_set, max_fd);
1224                DBG(("FD_SET(%d) (listening)", l->sock));
1225        }
1226
1227        if (num_workers(ctx) == 1)
1228                nowait = multiplex_worker_sockets(first_worker(ctx), max_fd,
1229                    read_set, write_set);
1230
1231        return (nowait);
1232}
1233
1234
1235static void
1236process_worker_sockets(struct worker *worker, fd_set *read_set)
1237{
1238        struct llhead   *lp, *tmp;
1239        int             cmd, skt[2], sock = worker->ctl[0];
1240        struct conn     *c;
1241
1242        /* Check if new socket is passed to us over the control socket */
1243        if (FD_ISSET(worker->ctl[0], read_set))
1244                while (recv(sock, (void *) &cmd, sizeof(cmd), 0) == sizeof(cmd))
1245                        switch (cmd) {
1246                        case CTL_PASS_SOCKET:
1247                                (void)recv(sock, (void *) &skt, sizeof(skt), 0);
1248                                add_socket(worker, skt[0], skt[1]);
1249                                break;
1250                        case CTL_WAKEUP:
1251                                (void)recv(sock, (void *) &c, sizeof(c), 0);
1252                                c->loc.flags &= FLAG_SUSPEND;
1253                                break;
1254                        default:
1255                                _shttpd_elog(E_FATAL, NULL, "ctx %p: ctl cmd %d",
1256                                    worker->ctx, cmd);
1257                                break;
1258                        }
1259
1260        /* Process all connections */
1261        LL_FOREACH_SAFE(&worker->connections, lp, tmp) {
1262                c = LL_ENTRY(lp, struct conn, link);
1263                process_connection(c, FD_ISSET(c->rem.chan.sock, read_set),
1264                    c->loc.io_class != NULL &&
1265                    ((c->loc.flags & FLAG_ALWAYS_READY)
1266#if !defined(NO_CGI)
1267                    || (c->loc.io_class == &_shttpd_io_cgi &&
1268                     FD_ISSET(c->loc.chan.fd, read_set))
1269#endif /* NO_CGI */
1270                    ));
1271        }
1272}
1273
1274/*
1275 * One iteration of server loop. This is the core of the data exchange.
1276 */
1277void
1278shttpd_poll(struct shttpd_ctx *ctx, int milliseconds)
1279{
1280        struct llhead   *lp;
1281        struct listener *l;
1282        fd_set          read_set, write_set;
1283        int             sock, max_fd = -1;
1284        struct usa      sa;
1285
1286        _shttpd_current_time = time(0);
1287        FD_ZERO(&read_set);
1288        FD_ZERO(&write_set);
1289
1290        if (shttpd_join(ctx, &read_set, &write_set, &max_fd))
1291                milliseconds = 0;
1292
1293        if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
1294                return;;
1295
1296        /* Check for incoming connections on listener sockets */
1297        LL_FOREACH(&ctx->listeners, lp) {
1298                l = LL_ENTRY(lp, struct listener, link);
1299                if (!FD_ISSET(l->sock, &read_set))
1300                        continue;
1301                do {
1302                        sa.len = sizeof(sa.u.sin);
1303                        if ((sock = accept(l->sock, &sa.u.sa, &sa.len)) != -1)
1304                                handle_connected_socket(ctx,&sa,sock,l->is_ssl);
1305                } while (sock != -1);
1306        }
1307
1308        if (num_workers(ctx) == 1)
1309                process_worker_sockets(first_worker(ctx), &read_set);
1310}
1311
1312/*
1313 * Deallocate shttpd object, free up the resources
1314 */
1315void
1316shttpd_fini(struct shttpd_ctx *ctx)
1317{
1318        size_t  i;
1319
1320        free_list(&ctx->workers, worker_destructor);
1321        free_list(&ctx->registered_uris, registered_uri_destructor);
1322        free_list(&ctx->acl, acl_destructor);
1323        free_list(&ctx->listeners, listener_destructor);
1324#if !defined(NO_SSI)
1325        free_list(&ctx->ssi_funcs, _shttpd_ssi_func_destructor);
1326#endif /* !NO_SSI */
1327
1328        for (i = 0; i < NELEMS(ctx->options); i++)
1329                if (ctx->options[i] != NULL)
1330                        free(ctx->options[i]);
1331
1332        if (ctx->access_log)            (void) fclose(ctx->access_log);
1333        if (ctx->error_log)             (void) fclose(ctx->error_log);
1334
1335        /* TODO: free SSL context */
1336
1337        free(ctx);
1338}
1339
1340/*
1341 * UNIX socketpair() implementation. Why? Because Windows does not have it.
1342 * Return 0 on success, -1 on error.
1343 */
1344int
1345shttpd_socketpair(int sp[2])
1346{
1347        struct sockaddr_in      sa;
1348        int                     sock, ret = -1;
1349        socklen_t               len = sizeof(sa);
1350
1351        sp[0] = sp[1] = -1;
1352
1353        (void) memset(&sa, 0, sizeof(sa));
1354        sa.sin_family           = AF_INET;
1355        sa.sin_port             = htons(0);
1356        sa.sin_addr.s_addr      = htonl(INADDR_LOOPBACK);
1357
1358        if ((sock = socket(AF_INET, SOCK_STREAM, 0)) != -1 &&
1359            !bind(sock, (struct sockaddr *) &sa, len) &&
1360            !listen(sock, 1) &&
1361            !getsockname(sock, (struct sockaddr *) &sa, &len) &&
1362            (sp[0] = socket(AF_INET, SOCK_STREAM, 6)) != -1 &&
1363            !connect(sp[0], (struct sockaddr *) &sa, len) &&
1364            (sp[1] = accept(sock,(struct sockaddr *) &sa, &len)) != -1) {
1365
1366                /* Success */
1367                ret = 0;
1368        } else {
1369
1370                /* Failure, close descriptors */
1371                if (sp[0] != -1)
1372                        (void) closesocket(sp[0]);
1373                if (sp[1] != -1)
1374                        (void) closesocket(sp[1]);
1375        }
1376
1377        (void) closesocket(sock);
1378        (void) _shttpd_set_non_blocking_mode(sp[0]);
1379        (void) _shttpd_set_non_blocking_mode(sp[1]);
1380
1381#ifndef _WIN32
1382        (void) fcntl(sp[0], F_SETFD, FD_CLOEXEC);
1383        (void) fcntl(sp[1], F_SETFD, FD_CLOEXEC);
1384#endif /* _WIN32*/
1385
1386        return (ret);
1387}
1388
1389static int isbyte(int n) { return (n >= 0 && n <= 255); }
1390
1391static int
1392set_inetd(struct shttpd_ctx *ctx, const char *flag)
1393{
1394        ctx = NULL; /* Unused */
1395
1396        if (_shttpd_is_true(flag)) {
1397                shttpd_set_option(ctx, "ports", NULL);
1398                (void) freopen("/dev/null", "a", stderr);
1399                add_socket(first_worker(ctx), 0, 0);
1400        }
1401
1402        return (TRUE);
1403}
1404
1405static int
1406set_uid(struct shttpd_ctx *ctx, const char *uid)
1407{
1408        struct passwd   *pw;
1409
1410        ctx = NULL; /* Unused */
1411
1412#if !defined(_WIN32)
1413        if ((pw = getpwnam(uid)) == NULL)
1414                _shttpd_elog(E_FATAL, 0, "%s: unknown user [%s]", __func__, uid);
1415        else if (setgid(pw->pw_gid) == -1)
1416                _shttpd_elog(E_FATAL, NULL, "%s: setgid(%s): %s",
1417                    __func__, uid, strerror(errno));
1418        else if (setuid(pw->pw_uid) == -1)
1419                _shttpd_elog(E_FATAL, NULL, "%s: setuid(%s): %s",
1420                    __func__, uid, strerror(errno));
1421#endif /* !_WIN32 */
1422        return (TRUE);
1423}
1424
1425static int
1426set_acl(struct shttpd_ctx *ctx, const char *s)
1427{
1428        struct acl      *acl = NULL;
1429        char            flag;
1430        int             len, a, b, c, d, n, mask;
1431
1432        /* Delete the old ACLs if any */
1433        free_list(&ctx->acl, acl_destructor);
1434
1435        FOR_EACH_WORD_IN_LIST(s, len) {
1436
1437                mask = 32;
1438
1439                if (sscanf(s, "%c%d.%d.%d.%d%n",&flag,&a,&b,&c,&d,&n) != 5) {
1440                        _shttpd_elog(E_FATAL, NULL, "[%s]: subnet must be "
1441                            "[+|-]x.x.x.x[/x]", s);
1442                } else if (flag != '+' && flag != '-') {
1443                        _shttpd_elog(E_FATAL, NULL, "flag must be + or -: [%s]", s);
1444                } else if (!isbyte(a)||!isbyte(b)||!isbyte(c)||!isbyte(d)) {
1445                        _shttpd_elog(E_FATAL, NULL, "bad ip address: [%s]", s);
1446                } else  if ((acl = malloc(sizeof(*acl))) == NULL) {
1447                        _shttpd_elog(E_FATAL, NULL, "%s", "cannot malloc subnet");
1448                } else if (sscanf(s + n, "/%d", &mask) == 0) { 
1449                        /* Do nothing, no mask specified */
1450                } else if (mask < 0 || mask > 32) {
1451                        _shttpd_elog(E_FATAL, NULL, "bad subnet mask: %d [%s]", n, s);
1452                }
1453
1454                acl->ip = (a << 24) | (b << 16) | (c << 8) | d;
1455                acl->mask = mask ? 0xffffffffU << (32 - mask) : 0;
1456                acl->flag = flag;
1457                LL_TAIL(&ctx->acl, &acl->link);
1458        }
1459
1460        return (TRUE);
1461}
1462
1463#ifndef NO_SSL
1464/*
1465 * Dynamically load SSL library. Set up ctx->ssl_ctx pointer.
1466 */
1467static int
1468set_ssl(struct shttpd_ctx *ctx, const char *pem)
1469{
1470        SSL_CTX         *CTX;
1471        void            *lib;
1472        struct ssl_func *fp;
1473        int             retval = FALSE;
1474
1475        /* Load SSL library dynamically */
1476        if ((lib = dlopen(SSL_LIB, RTLD_LAZY)) == NULL) {
1477                _shttpd_elog(E_LOG, NULL, "set_ssl: cannot load %s", SSL_LIB);
1478                return (FALSE);
1479        }
1480
1481        for (fp = ssl_sw; fp->name != NULL; fp++)
1482                if ((fp->ptr.v_void = dlsym(lib, fp->name)) == NULL) {
1483                        _shttpd_elog(E_LOG, NULL,"set_ssl: cannot find %s", fp->name);
1484                        return (FALSE);
1485                }
1486
1487        /* Initialize SSL crap */
1488        SSL_library_init();
1489
1490        if ((CTX = SSL_CTX_new(SSLv23_server_method())) == NULL)
1491                _shttpd_elog(E_LOG, NULL, "SSL_CTX_new error");
1492        else if (SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
1493                _shttpd_elog(E_LOG, NULL, "cannot open %s", pem);
1494        else if (SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0)
1495                _shttpd_elog(E_LOG, NULL, "cannot open %s", pem);
1496        else
1497                retval = TRUE;
1498
1499        ctx->ssl_ctx = CTX;
1500
1501        return (retval);
1502}
1503#endif /* NO_SSL */
1504
1505static int
1506open_log_file(FILE **fpp, const char *path)
1507{
1508        int     retval = TRUE;
1509
1510        if (*fpp != NULL)
1511                (void) fclose(*fpp);
1512
1513        if (path == NULL) {
1514                *fpp = NULL;
1515        } else if ((*fpp = fopen(path, "a")) == NULL) {
1516                _shttpd_elog(E_LOG, NULL, "cannot open log file %s: %s",
1517                    path, strerror(errno));
1518                retval = FALSE;
1519        }
1520
1521        return (retval);
1522}
1523
1524static int set_alog(struct shttpd_ctx *ctx, const char *path) {
1525        return (open_log_file(&ctx->access_log, path));
1526}
1527
1528static int set_elog(struct shttpd_ctx *ctx, const char *path) {
1529        return (open_log_file(&ctx->error_log, path));
1530}
1531
1532static void show_cfg_page(struct shttpd_arg *arg);
1533
1534static int
1535set_cfg_uri(struct shttpd_ctx *ctx, const char *uri)
1536{
1537        free_list(&ctx->registered_uris, &registered_uri_destructor);
1538
1539        if (uri != NULL)
1540                shttpd_register_uri(ctx, uri, &show_cfg_page, ctx);
1541
1542        return (TRUE);
1543}
1544
1545static struct worker *
1546add_worker(struct shttpd_ctx *ctx)
1547{
1548        struct worker   *worker;
1549
1550        if ((worker = calloc(1, sizeof(*worker))) == NULL)
1551                _shttpd_elog(E_FATAL, NULL, "Cannot allocate worker");
1552        LL_INIT(&worker->connections);
1553        worker->ctx = ctx;
1554        (void) shttpd_socketpair(worker->ctl);
1555        LL_TAIL(&ctx->workers, &worker->link);
1556
1557        return (worker);
1558}
1559
1560#if !defined(NO_THREADS)
1561static void
1562poll_worker(struct worker *worker, int milliseconds)
1563{
1564        fd_set          read_set, write_set;
1565        int             max_fd = -1;
1566
1567        FD_ZERO(&read_set);
1568        FD_ZERO(&write_set);
1569
1570        if (multiplex_worker_sockets(worker, &max_fd, &read_set, &write_set))
1571                milliseconds = 0;
1572
1573        if (do_select(max_fd, &read_set, &write_set, milliseconds) < 0)
1574                return;;
1575
1576        process_worker_sockets(worker, &read_set);
1577}
1578
1579static void
1580worker_function(void *param)
1581{
1582        struct worker *worker = param;
1583
1584        while (worker->exit_flag == 0)
1585                poll_worker(worker, 1000 * 10);
1586
1587        free_list(&worker->connections, connection_desctructor);
1588        free(worker);
1589}
1590
1591static int
1592set_workers(struct shttpd_ctx *ctx, const char *value)
1593{
1594        int             new_num, old_num;
1595        struct llhead   *lp, *tmp;
1596        struct worker   *worker;
1597
1598        new_num = atoi(value);
1599        old_num = 0;
1600        LL_FOREACH(&ctx->workers, lp)
1601                old_num++;
1602
1603        if (new_num == 1) {
1604                if (old_num > 1)
1605                        /* Stop old threads */
1606                        LL_FOREACH_SAFE(&ctx->workers, lp, tmp) {
1607                                worker = LL_ENTRY(lp, struct worker, link);
1608                                LL_DEL(&worker->link);
1609                                worker = LL_ENTRY(lp, struct worker, link);
1610                                worker->exit_flag = 1;
1611                        }
1612                (void) add_worker(ctx);
1613        } else {
1614                /* FIXME: we cannot here reduce the number of threads */
1615                while (new_num > 1 && new_num > old_num) {
1616                        worker = add_worker(ctx);
1617                        _beginthread(worker_function, 0, worker);
1618                        old_num++;
1619                }
1620        }
1621
1622        return (TRUE);
1623}
1624#endif /* NO_THREADS */
1625
1626static const struct opt {
1627        int             index;          /* Index in shttpd_ctx          */
1628        const char      *name;          /* Option name in config file   */
1629        const char      *description;   /* Description                  */
1630        const char      *default_value; /* Default option value         */
1631        int (*setter)(struct shttpd_ctx *, const char *);
1632} known_options[] = {
1633        {OPT_ROOT, "root", "\tWeb root directory", ".", NULL},
1634        {OPT_INDEX_FILES, "index_files", "Index files", INDEX_FILES, NULL},
1635#ifndef NO_SSL
1636        {OPT_SSL_CERTIFICATE, "ssl_cert", "SSL certificate file", NULL,set_ssl},
1637#endif /* NO_SSL */
1638        {OPT_PORTS, "ports", "Listening ports", LISTENING_PORTS, set_ports},
1639        {OPT_DIR_LIST, "dir_list", "Directory listing", "yes", NULL},
1640        {OPT_CFG_URI, "cfg_uri", "Config uri", NULL, set_cfg_uri},
1641        {OPT_PROTECT, "protect", "URI to htpasswd mapping", NULL, NULL},
1642#ifndef NO_CGI
1643        {OPT_CGI_EXTENSIONS, "cgi_ext", "CGI extensions", CGI_EXT, NULL},
1644        {OPT_CGI_INTERPRETER, "cgi_interp", "CGI interpreter", NULL, NULL},
1645        {OPT_CGI_ENVIRONMENT, "cgi_env", "Additional CGI env vars", NULL, NULL},
1646#endif /* NO_CGI */
1647        {OPT_SSI_EXTENSIONS, "ssi_ext", "SSI extensions", SSI_EXT, NULL},
1648#ifndef NO_AUTH
1649        {OPT_AUTH_REALM, "auth_realm", "Authentication domain name",REALM,NULL},
1650        {OPT_AUTH_GPASSWD, "auth_gpass", "Global passwords file", NULL, NULL},
1651        {OPT_AUTH_PUT, "auth_PUT", "PUT,DELETE auth file", NULL, NULL},
1652#endif /* !NO_AUTH */
1653#ifdef _WIN32
1654        {OPT_SERVICE, "service", "Manage WinNNT service (install"
1655            "|uninstall)", NULL, _shttpd_set_nt_service},
1656        {OPT_HIDE, "systray", "Hide console, show icon on systray",
1657                "no", _shttpd_set_systray},
1658#else
1659        {OPT_INETD, "inetd", "Inetd mode", "no", set_inetd},
1660        {OPT_UID, "uid", "\tRun as user", NULL, set_uid},
1661#endif /* _WIN32 */
1662        {OPT_ACCESS_LOG, "access_log", "Access log file", NULL, set_alog},
1663        {OPT_ERROR_LOG, "error_log", "Error log file", NULL, set_elog},
1664        {OPT_MIME_TYPES, "mime_types", "Additional mime types list", NULL,NULL},
1665        {OPT_ALIASES, "aliases", "Path=URI mappings", NULL, NULL},
1666        {OPT_ACL, "acl", "\tAllow/deny IP addresses/subnets", NULL, set_acl},
1667#if !defined(NO_THREADS)
1668        {OPT_THREADS, "threads", "Number of worker threads", "1", set_workers},
1669#endif /* !NO_THREADS */
1670        {-1, NULL, NULL, NULL, NULL}
1671};
1672
1673static const struct opt *
1674find_opt(const char *opt_name)
1675{
1676        int     i;
1677
1678        for (i = 0; known_options[i].name != NULL; i++)
1679                if (!strcmp(opt_name, known_options[i].name))
1680                        return (known_options + i);
1681
1682        _shttpd_elog(E_FATAL, NULL, "no such option: [%s]", opt_name);
1683
1684        /* UNREACHABLE */
1685        return (NULL);
1686}
1687
1688int
1689shttpd_set_option(struct shttpd_ctx *ctx, const char *opt, const char *val)
1690{
1691        const struct opt        *o = find_opt(opt);
1692        int                     retval = TRUE;
1693
1694        /* Call option setter first, so it can use both new and old values */
1695        if (o->setter != NULL)
1696                retval = o->setter(ctx, val);
1697
1698        /* Free old value if any */
1699        if (ctx->options[o->index] != NULL)
1700                free(ctx->options[o->index]);
1701       
1702        /* Set new option value */
1703        ctx->options[o->index] = val ? _shttpd_strdup(val) : NULL;
1704
1705        return (retval);
1706}
1707
1708static void
1709show_cfg_page(struct shttpd_arg *arg)
1710{
1711        struct shttpd_ctx       *ctx = arg->user_data;
1712        char                    opt_name[20], value[BUFSIZ];
1713        const struct opt        *o;
1714
1715        opt_name[0] = value[0] = '\0';
1716
1717        if (!strcmp(shttpd_get_env(arg, "REQUEST_METHOD"), "POST")) {
1718                if (arg->flags & SHTTPD_MORE_POST_DATA)
1719                        return;
1720                (void) shttpd_get_var("o", arg->in.buf, arg->in.len,
1721                    opt_name, sizeof(opt_name));
1722                (void) shttpd_get_var("v", arg->in.buf, arg->in.len,
1723                    value, sizeof(value));
1724                shttpd_set_option(ctx, opt_name, value[0] ? value : NULL);
1725        }
1726
1727        shttpd_printf(arg, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"
1728            "<html><body><h1>SHTTPD v. %s</h1>", shttpd_version());
1729
1730        shttpd_printf(arg, "%s", "<table border=1"
1731            "<tr><th>Option</th><th>Description</th>"
1732            "<th colspan=2>Value</th></tr>");
1733
1734        if (opt_name[0] != '\0' && value[0] != '\0')
1735                shttpd_printf(arg, "<p style='color: green'>Saved: %s=%s</p>",
1736                    opt_name, value[0] ? value : "NULL");
1737
1738
1739        for (o = known_options; o->name != NULL; o++) {
1740                shttpd_printf(arg,
1741                    "<form method=post><tr><td>%s</td><td>%s</td>"
1742                    "<input type=hidden name=o value='%s'>"
1743                    "<td><input type=text name=v value='%s'></td>"
1744                    "<td><input type=submit value=save></td></form></tr>",
1745                    o->name, o->description, o->name,
1746                    ctx->options[o->index] ? ctx->options[o->index] : "");
1747        }
1748
1749        shttpd_printf(arg, "%s", "</table></body></html>");
1750        arg->flags |= SHTTPD_END_OF_OUTPUT;
1751}
1752
1753/*
1754 * Show usage string and exit.
1755 */
1756void
1757_shttpd_usage(const char *prog)
1758{
1759        const struct opt        *o;
1760
1761        (void) fprintf(stderr,
1762            "SHTTPD version %s (c) Sergey Lyubka\n"
1763            "usage: %s [options] [config_file]\n", VERSION, prog);
1764
1765#if !defined(NO_AUTH)
1766        fprintf(stderr, "  -A <htpasswd_file> <realm> <user> <passwd>\n");
1767#endif /* NO_AUTH */
1768
1769        for (o = known_options; o->name != NULL; o++) {
1770                (void) fprintf(stderr, "  -%s\t%s", o->name, o->description);
1771                if (o->default_value != NULL)
1772                        fprintf(stderr, " (default: %s)", o->default_value);
1773                fputc('\n', stderr);
1774        }
1775
1776        exit(EXIT_FAILURE);
1777}
1778
1779static void
1780set_opt(struct shttpd_ctx *ctx, const char *opt, const char *value)
1781{
1782        const struct opt        *o;
1783
1784        o = find_opt(opt);
1785        if (ctx->options[o->index] != NULL)
1786                free(ctx->options[o->index]);
1787        ctx->options[o->index] = _shttpd_strdup(value);
1788}
1789
1790static void
1791process_command_line_arguments(struct shttpd_ctx *ctx, char *argv[])
1792{
1793        const char              *config_file = CONFIG_FILE;
1794        char                    line[BUFSIZ], opt[BUFSIZ],
1795                                val[BUFSIZ], path[FILENAME_MAX], *p;
1796        FILE                    *fp;
1797        size_t                  i, line_no = 0;
1798
1799        /* First find out, which config file to open */
1800        for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
1801                if (argv[i + 1] == NULL)
1802                        _shttpd_usage(argv[0]);
1803
1804        if (argv[i] != NULL && argv[i + 1] != NULL) {
1805                /* More than one non-option arguments are given w*/
1806                _shttpd_usage(argv[0]);
1807        } else if (argv[i] != NULL) {
1808                /* Just one non-option argument is given, this is config file */
1809                config_file = argv[i];
1810        } else {
1811                /* No config file specified. Look for one where shttpd lives */
1812                if ((p = strrchr(argv[0], DIRSEP)) != 0) {
1813                        _shttpd_snprintf(path, sizeof(path), "%.*s%s",
1814                            p - argv[0] + 1, argv[0], config_file);
1815                        config_file = path;
1816                }
1817        }
1818
1819        fp = fopen(config_file, "r");
1820
1821        /* If config file was set in command line and open failed, exit */
1822        if (fp == NULL && argv[i] != NULL)
1823                _shttpd_elog(E_FATAL, NULL, "cannot open config file %s: %s",
1824                    config_file, strerror(errno));
1825
1826        if (fp != NULL) {
1827
1828                _shttpd_elog(E_LOG, NULL, "Loading config file %s", config_file);
1829
1830                /* Loop over the lines in config file */
1831                while (fgets(line, sizeof(line), fp) != NULL) {
1832
1833                        line_no++;
1834
1835                        /* Ignore empty lines and comments */
1836                        if (line[0] == '#' || line[0] == '\n')
1837                                continue;
1838
1839                        if (sscanf(line, "%s %[^\n#]", opt, val) != 2)
1840                                _shttpd_elog(E_FATAL, NULL, "line %d in %s is invalid",
1841                                    line_no, config_file);
1842
1843                        set_opt(ctx, opt, val);
1844                }
1845
1846                (void) fclose(fp);
1847        }
1848
1849        /* Now pass through the command line options */
1850        for (i = 1; argv[i] != NULL && argv[i][0] == '-'; i += 2)
1851                set_opt(ctx, &argv[i][1], argv[i + 1]);
1852}
1853
1854struct shttpd_ctx *
1855shttpd_init(int argc, char *argv[])
1856{
1857        struct shttpd_ctx       *ctx;
1858        struct tm               *tm;
1859        const struct opt        *o;
1860
1861        if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
1862                _shttpd_elog(E_FATAL, NULL, "cannot allocate shttpd context");
1863
1864        LL_INIT(&ctx->registered_uris);
1865        LL_INIT(&ctx->error_handlers);
1866        LL_INIT(&ctx->acl);
1867        LL_INIT(&ctx->ssi_funcs);
1868        LL_INIT(&ctx->listeners);
1869        LL_INIT(&ctx->workers);
1870
1871        /* Initialize options. First pass: set default option values */
1872        for (o = known_options; o->name != NULL; o++)
1873                ctx->options[o->index] = o->default_value ?
1874                        _shttpd_strdup(o->default_value) : NULL;
1875
1876        /* Second and third passes: config file and argv */
1877        if (argc > 0 && argv != NULL)
1878                process_command_line_arguments(ctx, argv);
1879
1880        /* Call setter functions */
1881        for (o = known_options; o->name != NULL; o++)
1882                if (o->setter && ctx->options[o->index] != NULL)
1883                        if (o->setter(ctx, ctx->options[o->index]) == FALSE) {
1884                                shttpd_fini(ctx);
1885                                return (NULL);
1886                        }
1887
1888        _shttpd_current_time = time(NULL);
1889        tm = localtime(&_shttpd_current_time);
1890        _shttpd_tz_offset = 0;
1891
1892        if (num_workers(ctx) == 1)
1893                (void) add_worker(ctx);
1894#if 0
1895        tm->tm_gmtoff - 3600 * (tm->tm_isdst > 0 ? 1 : 0);
1896#endif
1897
1898#ifdef _WIN32
1899        {WSADATA data;  WSAStartup(MAKEWORD(2,2), &data);}
1900#endif /* _WIN32 */
1901
1902        return (ctx);
1903}
Note: See TracBrowser for help on using the repository browser.