/*
 * Pound - the reverse-proxy load-balancer
 * Copyright (C) 2002-2006 Apsis GmbH
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * Additionaly compiling, linking, and/or using OpenSSL is expressly allowed.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *
 * Contact information:
 * Apsis GmbH
 * P.O.Box
 * 8707 Uetikon am See
 * Switzerland
 * Tel: +41-44-920 4904
 * EMail: roseg@apsis.ch
 */

#ifndef MISS_FACILITYNAMES
#define SYSLOG_NAMES    1
#endif

#include    "pound.h"

#ifdef MISS_FACILITYNAMES

/* This is lifted verbatim from the Linux sys/syslog.h */

typedef struct _code {
	char	*c_name;
	int	c_val;
} CODE;

static CODE facilitynames[] = {
    { "auth", LOG_AUTH },
#ifdef  LOG_AUTHPRIV
    { "authpriv", LOG_AUTHPRIV },
#endif
    { "cron", LOG_CRON },
    { "daemon", LOG_DAEMON },
#ifdef  LOG_FTP
    { "ftp", LOG_FTP },
#endif
    { "kern", LOG_KERN },
    { "lpr", LOG_LPR },
    { "mail", LOG_MAIL },
    { "mark", 0 },                  /* never used! */
    { "news", LOG_NEWS },
    { "security", LOG_AUTH },       /* DEPRECATED */
    { "syslog", LOG_SYSLOG },
    { "user", LOG_USER },
    { "uucp", LOG_UUCP },
    { "local0", LOG_LOCAL0 },
    { "local1", LOG_LOCAL1 },
    { "local2", LOG_LOCAL2 },
    { "local3", LOG_LOCAL3 },
    { "local4", LOG_LOCAL4 },
    { "local5", LOG_LOCAL5 },
    { "local6", LOG_LOCAL6 },
    { "local7", LOG_LOCAL7 },
    { NULL, -1 }
};
#endif

static regex_t  Empty, Comment, User, Group, RootJail, Daemon, LogFacility, LogLevel, Alive, SSLEngine;
static regex_t  ListenHTTP, ListenHTTPS, End, Address, Port, Cert, xHTTP, Client, CheckURL;
static regex_t  Err414, Err500, Err501, Err503, MaxRequest, HeadRemove, RewriteLocation, RewriteDestination;
static regex_t  Service, URL, HeadRequire, HeadDeny, BackEnd, Priority, HAport, HAportAddr, Redirect, TimeOut;
static regex_t  Session, Type, TTL, ID;
static regex_t  ClientCert, AddHeader, Ciphers, CAlist, VerifyList, NoHTTPS11;

static regmatch_t   matches[5];

static char *xhttp[] = {
    "^(GET|POST|HEAD) ([^ ]+) HTTP/1.[01]$",
    "^(GET|POST|HEAD|PUT|DELETE) ([^ ]+) HTTP/1.[01]$",
    "^(GET|POST|HEAD|PUT|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT) ([^ ]+) HTTP/1.[01]$",
    "^(GET|POST|HEAD|PUT|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|CONNECT) ([^ ]+) HTTP/1.[01]$",
    "^(GET|POST|HEAD|PUT|DELETE|LOCK|UNLOCK|PROPFIND|PROPPATCH|SEARCH|MKCOL|MOVE|COPY|OPTIONS|TRACE|MKACTIVITY|CHECKOUT|MERGE|REPORT|SUBSCRIBE|BPROPPATCH|POLL|BMOVE|BCOPY|BDELETE|CONNECT|RPC_IN_DATA|RPC_OUT_DATA) ([^ ]+) HTTP/1.[01]$",
};

/*
 * parse a back-end
 */
static BACKEND *
parse_be(FILE *const f_conf)
{
    char        lin[MAXBUF];
    BACKEND     *res;
    int         has_addr, has_port;
    struct hostent      *host;

    if((res = (BACKEND *)malloc(sizeof(BACKEND))) == NULL) {
        logmsg(LOG_ERR, "BackEnd config: out of memory - aborted");
        exit(1);
    }
    memset(res, 0, sizeof(BACKEND));
    res->be_type = BACK_END;
    res->domain = PF_UNSPEC;
    res->to = 15;
    res->alive = 1;
    memset(&res->addr, 0, sizeof(res->addr));
    res->priority = 1;
    memset(&res->HA, 0, sizeof(res->HA));
    res->url = NULL;
    res->next = NULL;
    has_addr = has_port = 0;
    while(fgets(lin, MAXBUF, f_conf)) {
        if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n')
            lin[strlen(lin) - 1] = '\0';
        if(!regexec(&Empty, lin, 4, matches, 0) || !regexec(&Comment, lin, 4, matches, 0)) {
            /* comment or empty line */
            continue;
        } else if(!regexec(&Address, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            if((host = gethostbyname(lin + matches[1].rm_so)) == NULL || host->h_addr_list[0] == NULL) {
                /* if we can't resolve it assume this is a UNIX domain socket */
                res->domain = PF_UNIX;
                res->addr.un.sun_family = AF_UNIX;
                strncpy(res->addr.un.sun_path, lin + matches[1].rm_so, sizeof(res->addr.un.sun_path) - 1);
            } else {
                /* this is a regular IP socket */
                res->domain = PF_INET;
                res->addr.in.sin_family = AF_INET;
                memcpy(&res->addr.in.sin_addr.s_addr, host->h_addr_list[0], sizeof(res->addr.in.sin_addr.s_addr));
            }
            has_addr = 1;
        } else if(!regexec(&Port, lin, 4, matches, 0)) {
            if(res->domain != PF_INET) {
                logmsg(LOG_ERR, "Port is supported only for INET back-ends\n");
                exit(1);
            }
            res->addr.in.sin_port = (in_port_t)htons(atoi(lin + matches[1].rm_so));
            has_port = 1;
        } else if(!regexec(&Priority, lin, 4, matches, 0)) {
            res->priority = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&TimeOut, lin, 4, matches, 0)) {
            res->to = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&HAport, lin, 4, matches, 0)) {
            if(res->domain != PF_INET) {
                logmsg(LOG_ERR, "HAport is supported only for INET back-ends\n");
                exit(1);
            }
            memcpy(&res->HA, &res->addr.in, sizeof(res->HA));
            res->HA.sin_port = (in_port_t)htons(atoi(lin + matches[1].rm_so));
        } else if(!regexec(&HAportAddr, lin, 4, matches, 0)) {
            if(res->domain != PF_INET) {
                logmsg(LOG_ERR, "HAport is supported only for INET back-ends\n");
                exit(1);
            }
            res->HA.sin_family = AF_INET;
            lin[matches[1].rm_eo] = '\0';
            if((host = gethostbyname(lin + matches[1].rm_so)) == NULL || host->h_addr_list[0] == NULL) {
                logmsg(LOG_ERR, "Unknown HAport host \"%s\"", lin + matches[1].rm_so);
                exit(1);
            }
            memcpy(&res->HA.sin_addr.s_addr, host->h_addr_list[0], sizeof(res->HA.sin_addr.s_addr));
            res->HA.sin_port = (in_port_t)htons(atoi(lin + matches[2].rm_so));
        } else if(!regexec(&End, lin, 4, matches, 0)) {
            if(!has_addr) {
                logmsg(LOG_ERR, "BackEnd missing Address - aborted");
                exit(1);
            }
            if(res->domain == PF_INET && !has_port) {
                logmsg(LOG_ERR, "BackEnd missing Port - aborted");
                exit(1);
            }
            return res;
        } else {
            logmsg(LOG_ERR, "unknown directive \"%s\" - aborted", lin);
            exit(1);
        }
    }

    logmsg(LOG_ERR, "BackEnd premature EOF");
    exit(1);
    return NULL;
}

/*
 * parse a session
 */
static void
parse_sess(FILE *const f_conf, SERVICE *const svc)
{
    char        lin[MAXBUF], *cp;

    while(fgets(lin, MAXBUF, f_conf)) {
        if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n')
            lin[strlen(lin) - 1] = '\0';
        if(!regexec(&Empty, lin, 4, matches, 0) || !regexec(&Comment, lin, 4, matches, 0)) {
            /* comment or empty line */
            continue;
        } else if(!regexec(&Type, lin, 4, matches, 0)) {
            if(svc->sess_type != SESS_NONE) {
                logmsg(LOG_ERR, "Multiple Session types in one Service - aborted");
                exit(1);
            }
            lin[matches[1].rm_eo] = '\0';
            cp = lin + matches[1].rm_so;
            if(!strcasecmp(cp, "IP"))
                svc->sess_type = SESS_IP;
            else if(!strcasecmp(cp, "COOKIE"))
                svc->sess_type = SESS_COOKIE;
            else if(!strcasecmp(cp, "PARM"))
                svc->sess_type = SESS_PARM;
            else if(!strcasecmp(cp, "BASIC"))
                svc->sess_type = SESS_BASIC;
            else if(!strcasecmp(cp, "HEADER"))
                svc->sess_type = SESS_HEADER;
            else {
                logmsg(LOG_ERR, "Unknown Session type \"%s\" - aborted", cp);
                exit(1);
            }
        } else if(!regexec(&TTL, lin, 4, matches, 0)) {
            svc->sess_ttl = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&ID, lin, 4, matches, 0)) {
            if(svc->sess_type != SESS_COOKIE && svc->sess_type != SESS_PARM && svc->sess_type != SESS_HEADER) {
                logmsg(LOG_ERR, "no ID permitted unless COOKIE/PARM/HEADER Session - aborted");
                exit(1);
            }
            lin[matches[1].rm_eo] = '\0';
            if((svc->sess_parm = strdup(lin + matches[1].rm_so)) == NULL) {
                logmsg(LOG_ERR, "ID config: out of memory - aborted");
                exit(1);
            }
        } else if(!regexec(&End, lin, 4, matches, 0)) {
            if(svc->sess_type == SESS_NONE) {
                logmsg(LOG_ERR, "Session type not defined - aborted");
                exit(1);
            }
            if(svc->sess_ttl <= 0) {
                logmsg(LOG_ERR, "Session TTL not defined - aborted");
                exit(1);
            }
            if((svc->sess_type == SESS_COOKIE || svc->sess_type == SESS_PARM || svc->sess_type == SESS_HEADER)
            && svc->sess_parm == NULL) {
                logmsg(LOG_ERR, "Session ID not defined - aborted");
                exit(1);
            }
            if(svc->sess_type == SESS_COOKIE) {
                snprintf(lin, MAXBUF - 1, "Cookie:.*[ \t]%s=([^;]*)", svc->sess_parm);
                if(regcomp(&svc->sess_pat, lin, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                    logmsg(LOG_ERR, "COOKIE pattern \"%s\" failed - aborted", lin);
                    exit(1);
                }
            } else if(svc->sess_type == SESS_PARM) {
                snprintf(lin, MAXBUF - 1, "[?&]%s=([^&;#]*)", svc->sess_parm);
                if(regcomp(&svc->sess_pat, lin, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                    logmsg(LOG_ERR, "PARM pattern \"%s\" failed - aborted", lin);
                    exit(1);
                }
            } else if(svc->sess_type == SESS_BASIC) {
                snprintf(lin, MAXBUF - 1, "Authorization:[ \t]*Basic[ \t]*([^ \t]*)[ \t]*");
                if(regcomp(&svc->sess_pat, lin, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                    logmsg(LOG_ERR, "BASIC pattern \"%s\" failed - aborted", lin);
                    exit(1);
                }
            } else if(svc->sess_type == SESS_HEADER) {
                snprintf(lin, MAXBUF - 1, "%s:[ \t]*([^ \t]*)[ \t]*", svc->sess_parm);
                if(regcomp(&svc->sess_pat, lin, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                    logmsg(LOG_ERR, "HEADER pattern \"%s\" failed - aborted", lin);
                    exit(1);
                }
            }
            return;
        } else {
            logmsg(LOG_ERR, "unknown directive \"%s\" - aborted", lin);
            exit(1);
        }
    }

    logmsg(LOG_ERR, "Session premature EOF");
    exit(1);
    return;
}

/*
 * parse a service
 */
static SERVICE *
parse_service(FILE *const f_conf)
{
    char        lin[MAXBUF];
    SERVICE     *res;
    BACKEND     *be;
    MATCHER     *m;

    if((res = (SERVICE *)malloc(sizeof(SERVICE))) == NULL) {
        logmsg(LOG_ERR, "Service config: out of memory - aborted");
        exit(1);
    }
    memset(res, 0, sizeof(SERVICE));
    res->sess_type = SESS_NONE;
    while(fgets(lin, MAXBUF, f_conf)) {
        if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n')
            lin[strlen(lin) - 1] = '\0';
        if(!regexec(&Empty, lin, 4, matches, 0) || !regexec(&Comment, lin, 4, matches, 0)) {
            /* comment or empty line */
            continue;
        } else if(!regexec(&URL, lin, 4, matches, 0)) {
            if(res->url) {
                for(m = res->url; m->next; m = m->next)
                    ;
                if((m->next = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "URL config: out of memory - aborted");
                    exit(1);
                }
                m = m->next;
            } else {
                if((res->url = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "URL config: out of memory - aborted");
                    exit(1);
                }
                m = res->url;
            }
            memset(m, 0, sizeof(MATCHER));
            lin[matches[1].rm_eo] = '\0';
            if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "URL bad pattern \"%s\" - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&HeadRequire, lin, 4, matches, 0)) {
            if(res->req_head) {
                for(m = res->req_head; m->next; m = m->next)
                    ;
                if((m->next = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadRequire config: out of memory - aborted");
                    exit(1);
                }
                m = m->next;
            } else {
                if((res->req_head = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadRequire config: out of memory - aborted");
                    exit(1);
                }
                m = res->req_head;
            }
            memset(m, 0, sizeof(MATCHER));
            lin[matches[1].rm_eo] = '\0';
            if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "HeadRequire bad pattern \"%s\" - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&HeadDeny, lin, 4, matches, 0)) {
            if(res->deny_head) {
                for(m = res->deny_head; m->next; m = m->next)
                    ;
                if((m->next = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadDeny config: out of memory - aborted");
                    exit(1);
                }
                m = m->next;
            } else {
                if((res->deny_head = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadDeny config: out of memory - aborted");
                    exit(1);
                }
                m = res->deny_head;
            }
            memset(m, 0, sizeof(MATCHER));
            lin[matches[1].rm_eo] = '\0';
            if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "HeadDeny bad pattern \"%s\" - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&Redirect, lin, 4, matches, 0)) {
            if(res->backends) {
                for(be = res->backends; be->next; be = be->next)
                    ;
                if((be->next = (BACKEND *)malloc(sizeof(BACKEND))) == NULL) {
                    logmsg(LOG_ERR, "Redirect config: out of memory - aborted");
                    exit(1);
                }
                be = be->next;
            } else {
                if((res->backends = (BACKEND *)malloc(sizeof(BACKEND))) == NULL) {
                    logmsg(LOG_ERR, "Redirect config: out of memory - aborted");
                    exit(1);
                }
                be = res->backends;
            }
            memset(be, 0, sizeof(BACKEND));
            be->be_type = REDIRECTOR;
            be->priority = 1;
            be->alive = 1;
            lin[matches[1].rm_eo] = '\0';
            if((be->url = strdup(lin + matches[1].rm_so)) == NULL) {
                logmsg(LOG_ERR, "Redirector config: out of memory - aborted");
                exit(1);
            }
        } else if(!regexec(&BackEnd, lin, 4, matches, 0)) {
            if(res->backends) {
                for(be = res->backends; be->next; be = be->next)
                    ;
                be->next = parse_be(f_conf);
            } else
                res->backends = parse_be(f_conf);
        } else if(!regexec(&Session, lin, 4, matches, 0)) {
            parse_sess(f_conf, res);
        } else if(!regexec(&End, lin, 4, matches, 0)) {
            for(be = res->backends; be; be = be->next)
                res->tot_pri += be->priority;
            return res;
        } else {
            logmsg(LOG_ERR, "unknown directive \"%s\" - aborted", lin);
            exit(1);
        }
    }

    logmsg(LOG_ERR, "Service premature EOF");
    exit(1);
    return NULL;
}

/*
 * return the file contents as a string
 */
static char *
file2str(const char *fname)
{
    char    *res;
    struct stat st;
    int     fin;

    if(stat(fname, &st)) {
        logmsg(LOG_ERR, "can't stat Err file \"%s\" (%s) - aborted", fname, strerror(errno));
        exit(1);
    }
    if((fin = open(fname, O_RDONLY)) < 0) {
        logmsg(LOG_ERR, "can't open Err file \"%s\" (%s) - aborted", fname, strerror(errno));
        exit(1);
    }
    if((res = malloc(st.st_size + 1)) == NULL) {
        logmsg(LOG_ERR, "can't alloc Err file \"%s\" (out of memory) - aborted", fname);
        exit(1);
    }
    if(read(fin, res, st.st_size) != st.st_size) {
        logmsg(LOG_ERR, "can't read Err file \"%s\" (%s) - aborted", fname, strerror(errno));
        exit(1);
    }
    res[st.st_size] = '\0';
    close(fin);
    return res;
}

/*
 * parse an HTTP listener
 */
static LISTENER *
parse_HTTP(FILE *const f_conf)
{
    char        lin[MAXBUF];
    LISTENER    *res;
    SERVICE     *svc;
    MATCHER     *m;
    struct hostent      *host;
    int         has_addr, has_port;

    if((res = (LISTENER *)malloc(sizeof(LISTENER))) == NULL) {
        logmsg(LOG_ERR, "ListenHTTP config: out of memory - aborted");
        exit(1);
    }
    memset(res, 0, sizeof(LISTENER));
    res->to = 10;
    res->rewr_loc = 1;
    res->err414 = "Request URI is too long";
    res->err500 = "An internal server error occurred. Please try again later.";
    res->err501 = "This method may not be used.";
    res->err503 = "The service is not available. Please try again later.";
    if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
        logmsg(LOG_ERR, "xHTTP bad default pattern - aborted");
        exit(1);
    }
    if(regcomp(&res->url_pat, ".*", REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
        logmsg(LOG_ERR, "CheckURL bad default pattern - aborted");
        exit(1);
    }
    has_addr = has_port = 0;
    while(fgets(lin, MAXBUF, f_conf)) {
        if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n')
            lin[strlen(lin) - 1] = '\0';
        if(!regexec(&Empty, lin, 4, matches, 0) || !regexec(&Comment, lin, 4, matches, 0)) {
            /* comment or empty line */
            continue;
        } else if(!regexec(&Address, lin, 4, matches, 0)) {
            res->addr.sin_family = AF_INET;
            lin[matches[1].rm_eo] = '\0';
            if((host = gethostbyname(lin + matches[1].rm_so)) == NULL || host->h_addr_list[0] == NULL) {
                logmsg(LOG_ERR, "Unknown Listener address \"%s\"", lin + matches[1].rm_so);
                exit(1);
            }
            memcpy(&res->addr.sin_addr.s_addr, host->h_addr_list[0], sizeof(res->addr.sin_addr.s_addr));
            has_addr = 1;
        } else if(!regexec(&Port, lin, 4, matches, 0)) {
            res->addr.sin_port = (in_port_t)htons(atoi(lin + matches[1].rm_so));
            has_port = 1;
        } else if(!regexec(&xHTTP, lin, 4, matches, 0)) {
            int n;

            n = atoi(lin + matches[1].rm_so);
            regfree(&res->verb);
            if(regcomp(&res->verb, xhttp[n], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "xHTTP bad pattern %d - aborted", n);
                exit(1);
            }
        } else if(!regexec(&Client, lin, 4, matches, 0)) {
            res->to = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&CheckURL, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            regfree(&res->url_pat);
            if(regcomp(&res->url_pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "CheckURL bad pattern \"%s\" - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&Err414, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err414 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&Err500, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err500 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&Err501, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err501 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&Err503, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err503 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&MaxRequest, lin, 4, matches, 0)) {
            res->max_req = atol(lin + matches[1].rm_so);
        } else if(!regexec(&HeadRemove, lin, 4, matches, 0)) {
            if(res->head_off) {
                for(m = res->head_off; m->next; m = m->next)
                    ;
                if((m->next = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadRemove config: out of memory - aborted");
                    exit(1);
                }
                m = m->next;
            } else {
                if((res->head_off = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadRemove config: out of memory - aborted");
                    exit(1);
                }
                m = res->head_off;
            }
            memset(m, 0, sizeof(MATCHER));
            lin[matches[1].rm_eo] = '\0';
            if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "HeadRemove bad pattern \"%s\" - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&RewriteLocation, lin, 4, matches, 0)) {
            res->rewr_loc = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&RewriteDestination, lin, 4, matches, 0)) {
            res->rewr_dest = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&Service, lin, 4, matches, 0)) {
            if(res->services == NULL)
                res->services = parse_service(f_conf);
            else {
                for(svc = res->services; svc->next; svc = svc->next)
                    ;
                svc->next = parse_service(f_conf);
            }
        } else if(!regexec(&End, lin, 4, matches, 0)) {
            if(!has_addr || !has_port) {
                logmsg(LOG_ERR, "ListenHTTP missing Address or Port - aborted");
                exit(1);
            }
            return res;
        } else {
            logmsg(LOG_ERR, "unknown directive \"%s\" - aborted", lin);
            exit(1);
        }
    }

    logmsg(LOG_ERR, "ListenHTTP premature EOF");
    exit(1);
    return NULL;
}
/*
 * Dummy certificate verification - always OK
 */
static int
verify_OK(int pre_ok, X509_STORE_CTX *ctx)
{
    return 1;
}

/*
 * parse an HTTPS listener
 */
static LISTENER *
parse_HTTPS(FILE *const f_conf)
{
    char        lin[MAXBUF];
    LISTENER    *res;
    SERVICE     *svc;
    MATCHER     *m;
    struct hostent      *host;
    int         has_addr, has_port, has_cert;

    if((res = (LISTENER *)malloc(sizeof(LISTENER))) == NULL) {
        logmsg(LOG_ERR, "ListenHTTPS config: out of memory - aborted");
        exit(1);
    }
    memset(res, 0, sizeof(LISTENER));
    if((res->ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) {
        logmsg(LOG_ERR, "SSL_CTX_new failed - aborted");
        exit(1);
    }

    res->to = 10;
    res->rewr_loc = 1;
    res->err414 = "Request URI is too long";
    res->err500 = "An internal server error occurred. Please try again later.";
    res->err501 = "This method may not be used.";
    res->err503 = "The service is not available. Please try again later.";
    if(regcomp(&res->verb, xhttp[0], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
        logmsg(LOG_ERR, "xHTTP bad default pattern - aborted");
        exit(1);
    }
    if(regcomp(&res->url_pat, ".*", REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
        logmsg(LOG_ERR, "CheckURL bad default pattern - aborted");
        exit(1);
    }
    has_addr = has_port = has_cert = 0;
    while(fgets(lin, MAXBUF, f_conf)) {
        if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n')
            lin[strlen(lin) - 1] = '\0';
        if(!regexec(&Empty, lin, 4, matches, 0) || !regexec(&Comment, lin, 4, matches, 0)) {
            /* comment or empty line */
            continue;
        } else if(!regexec(&Address, lin, 4, matches, 0)) {
            res->addr.sin_family = AF_INET;
            lin[matches[1].rm_eo] = '\0';
            if((host = gethostbyname(lin + matches[1].rm_so)) == NULL || host->h_addr_list[0] == NULL) {
                logmsg(LOG_ERR, "Unknown Listener address \"%s\"", lin + matches[1].rm_so);
                exit(1);
            }
            memcpy(&res->addr.sin_addr.s_addr, host->h_addr_list[0], sizeof(res->addr.sin_addr.s_addr));
            has_addr = 1;
        } else if(!regexec(&Port, lin, 4, matches, 0)) {
            res->addr.sin_port = (in_port_t)htons(atoi(lin + matches[1].rm_so));
            has_port = 1;
        } else if(!regexec(&xHTTP, lin, 4, matches, 0)) {
            int n;

            n = atoi(lin + matches[1].rm_so);
            regfree(&res->verb);
            if(regcomp(&res->verb, xhttp[n], REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "xHTTP bad pattern %d - aborted", n);
                exit(1);
            }
        } else if(!regexec(&Client, lin, 4, matches, 0)) {
            res->to = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&CheckURL, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            regfree(&res->url_pat);
            if(regcomp(&res->url_pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "CheckURL bad pattern \"%s\" - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&Err414, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err414 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&Err500, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err500 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&Err501, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err501 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&Err503, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            res->err503 = file2str(lin + matches[1].rm_so);
        } else if(!regexec(&MaxRequest, lin, 4, matches, 0)) {
            res->max_req = atol(lin + matches[1].rm_so);
        } else if(!regexec(&HeadRemove, lin, 4, matches, 0)) {
            if(res->head_off) {
                for(m = res->head_off; m->next; m = m->next)
                    ;
                if((m->next = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadRemove config: out of memory - aborted");
                    exit(1);
                }
                m = m->next;
            } else {
                if((res->head_off = (MATCHER *)malloc(sizeof(MATCHER))) == NULL) {
                    logmsg(LOG_ERR, "HeadRemove config: out of memory - aborted");
                    exit(1);
                }
                m = res->head_off;
            }
            memset(m, 0, sizeof(MATCHER));
            lin[matches[1].rm_eo] = '\0';
            if(regcomp(&m->pat, lin + matches[1].rm_so, REG_ICASE | REG_NEWLINE | REG_EXTENDED)) {
                logmsg(LOG_ERR, "HeadRemove bad pattern \"%s\" - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&RewriteLocation, lin, 4, matches, 0)) {
            res->rewr_loc = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&RewriteDestination, lin, 4, matches, 0)) {
            res->rewr_dest = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&Cert, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            if(SSL_CTX_use_certificate_chain_file(res->ctx, lin + matches[1].rm_so) != 1) {
                logmsg(LOG_ERR, "SSL_CTX_use_certificate_chain_file \"%s\" failed - aborted",
                    lin + matches[1].rm_so);
                exit(1);
            }
            if(SSL_CTX_use_PrivateKey_file(res->ctx, lin + matches[1].rm_so, SSL_FILETYPE_PEM) != 1) {
                logmsg(LOG_ERR, "SSL_CTX_use_PrivateKey_file \"%s\" failed - aborted",
                    lin + matches[1].rm_so);
                exit(1);
            }
            if(SSL_CTX_check_private_key(res->ctx) != 1) {
                logmsg(LOG_ERR, "SSL_CTX_check_private_key \"%s\" failed - aborted",
                    lin + matches[1].rm_so);
                exit(1);
            }
            has_cert = 1;
        } else if(!regexec(&ClientCert, lin, 4, matches, 0)) {
            switch(res->clnt_check = atoi(lin + matches[1].rm_so)) {
            case 0:
                /* don't ask */
                SSL_CTX_set_verify(res->ctx, SSL_VERIFY_NONE, NULL);
                break;
            case 1:
                /* ask but OK if no client certificate */
                SSL_CTX_set_verify(res->ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, NULL);
                SSL_CTX_set_verify_depth(res->ctx, atoi(lin + matches[2].rm_so));
                break;
            case 2:
                /* ask and fail if no client certificate */
                SSL_CTX_set_verify(res->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
                SSL_CTX_set_verify_depth(res->ctx, atoi(lin + matches[2].rm_so));
                break;
            case 3:
                /* ask but do not verify client certificate */
                SSL_CTX_set_verify(res->ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_OK);
                SSL_CTX_set_verify_depth(res->ctx, atoi(lin + matches[2].rm_so));
                break;
            }
        } else if(!regexec(&AddHeader, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            if((res->ssl_head = strdup(lin + matches[1].rm_so)) == NULL) {
                logmsg(LOG_ERR, "AddHeader config: out of memory - aborted");
                exit(1);
            }
        } else if(!regexec(&Ciphers, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            SSL_CTX_set_cipher_list(res->ctx, lin + matches[1].rm_so);
        } else if(!regexec(&CAlist, lin, 4, matches, 0)) {
            STACK_OF(X509_NAME) *cert_names;

            lin[matches[1].rm_eo] = '\0';
            if((cert_names = SSL_load_client_CA_file(lin + matches[1].rm_so)) == NULL) {
                logmsg(LOG_ERR, "SSL_load_client_CA_file \"%s\" failed - aborted", lin + matches[1].rm_so);
                exit(1);
            }
            SSL_CTX_set_client_CA_list(res->ctx, cert_names);
        } else if(!regexec(&VerifyList, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            if(SSL_CTX_load_verify_locations(res->ctx, lin + matches[1].rm_so, NULL) != 1) {
                logmsg(LOG_ERR, "SSL_CTX_load_verify_locations \"%s\" failed - aborted", lin + matches[1].rm_so);
                exit(1);
            }
        } else if(!regexec(&NoHTTPS11, lin, 4, matches, 0)) {
            res->noHTTPS11 = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&Service, lin, 4, matches, 0)) {
            if(res->services == NULL)
                res->services = parse_service(f_conf);
            else {
                for(svc = res->services; svc->next; svc = svc->next)
                    ;
                svc->next = parse_service(f_conf);
            }
        } else if(!regexec(&End, lin, 4, matches, 0)) {
            X509_STORE  *store;

            if(!has_addr || !has_port || !has_cert) {
                logmsg(LOG_ERR, "ListenHTTPS missing Address, Port or Certificate - aborted");
                exit(1);
            }
            SSL_CTX_set_mode(res->ctx, SSL_MODE_AUTO_RETRY);
            SSL_CTX_set_options(res->ctx, SSL_OP_ALL);
            sprintf(lin, "%d-Pound-%l", getpid(), random());
            SSL_CTX_set_session_id_context(res->ctx, (unsigned char *)lin, strlen(lin));
            SSL_CTX_set_tmp_rsa_callback(res->ctx, RSA_tmp_callback);
#if HAVE_X509_STORE_SET_FLAGS
            /* add the CRL stuff */
            if((store = SSL_CTX_get_cert_store(res->ctx)) != NULL)
                X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK);
            else
                logmsg(LOG_WARNING, "SSL_CTX_get_cert_store failed!");
#endif
            return res;
        } else {
            logmsg(LOG_ERR, "unknown directive \"%s\" - aborted", lin);
            exit(1);
        }
    }

    logmsg(LOG_ERR, "ListenHTTPS premature EOF");
    exit(1);
    return NULL;
}

/*
 * parse the config file
 */
static void
parse_file(FILE *const f_conf)
{
    char        lin[MAXBUF];
    SERVICE     *svc;
    LISTENER    *lstn;
    int         i;
#if HAVE_OPENSSL_ENGINE_H
    ENGINE      *e;
#endif

    while(fgets(lin, MAXBUF, f_conf)) {
        if(strlen(lin) > 0 && lin[strlen(lin) - 1] == '\n')
            lin[strlen(lin) - 1] = '\0';
        if(!regexec(&Empty, lin, 4, matches, 0) || !regexec(&Comment, lin, 4, matches, 0)) {
            /* comment or empty line */
            continue;
        } else if(!regexec(&User, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            if((user = strdup(lin + matches[1].rm_so)) == NULL) {
                logmsg(LOG_ERR, "User config: out of memory - aborted");
                exit(1);
            }
        } else if(!regexec(&Group, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            if((group = strdup(lin + matches[1].rm_so)) == NULL) {
                logmsg(LOG_ERR, "Group config: out of memory - aborted");
                exit(1);
            }
        } else if(!regexec(&RootJail, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            if((root_jail = strdup(lin + matches[1].rm_so)) == NULL) {
                logmsg(LOG_ERR, "RootJail config: out of memory - aborted");
                exit(1);
            }
        } else if(!regexec(&Daemon, lin, 4, matches, 0)) {
            daemonize = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&LogFacility, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
            for(i = 0; facilitynames[i].c_name; i++)
                if(!strcmp(facilitynames[i].c_name, lin + matches[1].rm_so)) {
                    log_facility = facilitynames[i].c_val;
                    break;
                }
        } else if(!regexec(&LogLevel, lin, 4, matches, 0)) {
            log_level = atoi(lin + matches[1].rm_so);
        } else if(!regexec(&Alive, lin, 4, matches, 0)) {
            alive_to = atoi(lin + matches[1].rm_so);
#if HAVE_OPENSSL_ENGINE_H
        } else if(!regexec(&SSLEngine, lin, 4, matches, 0)) {
            lin[matches[1].rm_eo] = '\0';
#if OPENSSL_VERSION_NUMBER >= 0x00907000L
            ENGINE_load_builtin_engines();
#endif
            if (!(e = ENGINE_by_id(lin + matches[1].rm_so))) {
                logmsg(LOG_ERR, "could not find %s engine", lin + matches[1].rm_so);
                exit(1);
            }
            if(!ENGINE_init(e)) {
                ENGINE_free(e);
                logmsg(LOG_ERR, "could not init %s engine", lin + matches[1].rm_so);
                exit(1);
            }
            if(!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
                ENGINE_free(e);
                logmsg(LOG_ERR, "could not set all defaults");
                exit(1);
            }
            ENGINE_finish(e);
            ENGINE_free(e);
#endif
        } else if(!regexec(&ListenHTTP, lin, 4, matches, 0)) {
            if(listeners == NULL)
                listeners = parse_HTTP(f_conf);
            else {
                for(lstn = listeners; lstn->next; lstn = lstn->next)
                    ;
                lstn->next = parse_HTTP(f_conf);
            }
        } else if(!regexec(&ListenHTTPS, lin, 4, matches, 0)) {
            if(listeners == NULL)
                listeners = parse_HTTPS(f_conf);
            else {
                for(lstn = listeners; lstn->next; lstn = lstn->next)
                    ;
                lstn->next = parse_HTTPS(f_conf);
            }
        } else if(!regexec(&Service, lin, 4, matches, 0)) {
            if(services == NULL)
                services = parse_service(f_conf);
            else {
                for(svc = services; svc->next; svc = svc->next)
                    ;
                svc->next = parse_service(f_conf);
            }
        } else {
            logmsg(LOG_ERR, "unknown directive \"%s\" - aborted", lin);
            exit(1);
        }
    }
    return;
}

/*
 * prepare to parse the arguments/config file
 */
void
config_parse(const int argc, char **const argv)
{
    char    *conf_name;
    FILE    *f_conf;
    int     c_opt, check_only;

    if(regcomp(&Empty, "^[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Comment, "^[ \t]*#.*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&User, "^[ \t]*User[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Group, "^[ \t]*Group[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&RootJail, "^[ \t]*RootJail[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Daemon, "^[ \t]*Daemon[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&LogFacility, "^[ \t]*LogFacility[ \t]+([a-z0-9]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&LogLevel, "^[ \t]*LogLevel[ \t]+([0-4])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Alive, "^[ \t]*Alive[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&SSLEngine, "^[ \t]*SSLEngine[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&ListenHTTP, "^[ \t]*ListenHTTP[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&ListenHTTPS, "^[ \t]*ListenHTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&End, "^[ \t]*End[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Address, "^[ \t]*Address[ \t]+([^ \t]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Port, "^[ \t]*Port[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Cert, "^[ \t]*Cert[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&xHTTP, "^[ \t]*xHTTP[ \t]+([01234])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Client, "^[ \t]*Client[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&CheckURL, "^[ \t]*CheckURL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Err414, "^[ \t]*Err414[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Err500, "^[ \t]*Err500[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Err501, "^[ \t]*Err501[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Err503, "^[ \t]*Err503[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&MaxRequest, "^[ \t]*MaxRequest[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&HeadRemove, "^[ \t]*HeadRemove[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&RewriteLocation, "^[ \t]*RewriteLocation[ \t]+([012])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&RewriteDestination, "^[ \t]*RewriteDestination[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Service, "^[ \t]*Service[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&URL, "^[ \t]*URL[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&HeadRequire, "^[ \t]*HeadRequire[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&HeadDeny, "^[ \t]*HeadDeny[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&BackEnd, "^[ \t]*BackEnd[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Priority, "^[ \t]*Priority[ \t]+([1-9])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&TimeOut, "^[ \t]*TimeOut[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&HAport, "^[ \t]*HAport[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&HAportAddr, "^[ \t]*HAport[ \t]+([^ \t]+)[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Redirect, "^[ \t]*Redirect[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Session, "^[ \t]*Session[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Type, "^[ \t]*Type[ \t]+([^ \t]+)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&TTL, "^[ \t]*TTL[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&ID, "^[ \t]*ID[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&ClientCert, "^[ \t]*ClientCert[ \t]+([0-3])[ \t]+([1-9])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&AddHeader, "^[ \t]*AddHeader[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&Ciphers, "^[ \t]*Ciphers[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&CAlist, "^[ \t]*CAlist[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&VerifyList, "^[ \t]*VerifyList[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    || regcomp(&NoHTTPS11, "^[ \t]*NoHTTPS11[ \t]+([0-2])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
    ) {
        logmsg(LOG_ERR, "bad config Regex - aborted");
        exit(1);
    }

    opterr = 0;
    check_only = 0;
    conf_name = F_CONF;
    pid_name = F_PID;

    while((c_opt = getopt(argc, argv, "f:cvVp:")) > 0)
        switch(c_opt) {
        case 'f':
            conf_name = optarg;
            break;
        case 'p':
            pid_name = optarg;
            break;
        case 'c':
            check_only = 1;
            break;
        case 'v':
            print_log = 1;
            break;
        case 'V':
            print_log = 1;
            logmsg(LOG_DEBUG, "Version %s", VERSION);
            logmsg(LOG_DEBUG, "Exiting...");
            exit(0);
            break;
        default:
            logmsg(LOG_ERR, "bad flag -%c", optopt);
            exit(1);
            break;
        }
    if(optind < argc) {
        logmsg(LOG_ERR, "unknown extra arguments (%s...)", argv[optind]);
        exit(1);
    }

    if((f_conf = fopen(conf_name, "rt")) == NULL) {
        logmsg(LOG_ERR, "can't open configuration file \"%s\" (%s) - aborted", conf_name, strerror(errno));
        exit(1);
    }

    user = NULL;
    group = NULL;
    root_jail = NULL;

    alive_to = 30;
    daemonize = 1;
    log_facility = LOG_DAEMON;
    log_level = 1;

    services = NULL;
    listeners = NULL;

    parse_file(f_conf);

    fclose(f_conf);

    if(check_only) {
        logmsg(LOG_INFO, "Config file %s is OK", conf_name);
        exit(0);
    }

    if(listeners == NULL) {
        logmsg(LOG_ERR, "no listeners defined - aborted");
        exit(1);
    }

    regfree(&Empty);
    regfree(&Comment);
    regfree(&User);
    regfree(&Group);
    regfree(&RootJail);
    regfree(&Daemon);
    regfree(&LogFacility);
    regfree(&LogLevel);
    regfree(&Alive);
    regfree(&SSLEngine);
    regfree(&ListenHTTP);
    regfree(&ListenHTTPS);
    regfree(&End);
    regfree(&Address);
    regfree(&Port);
    regfree(&Cert);
    regfree(&xHTTP);
    regfree(&Client);
    regfree(&CheckURL);
    regfree(&Err414);
    regfree(&Err500);
    regfree(&Err501);
    regfree(&Err503);
    regfree(&MaxRequest);
    regfree(&HeadRemove);
    regfree(&RewriteLocation);
    regfree(&RewriteDestination);
    regfree(&Service);
    regfree(&URL);
    regfree(&HeadRequire);
    regfree(&HeadDeny);
    regfree(&BackEnd);
    regfree(&Priority);
    regfree(&TimeOut);
    regfree(&HAport);
    regfree(&HAportAddr);
    regfree(&Redirect);
    regfree(&Session);
    regfree(&Type);
    regfree(&TTL);
    regfree(&ID);
    regfree(&ClientCert);
    regfree(&AddHeader);
    regfree(&Ciphers);
    regfree(&CAlist);
    regfree(&VerifyList);
    regfree(&NoHTTPS11);

    return;
}
