diff --git a/qse/cmd/http/httpd.c b/qse/cmd/http/httpd.c index d81c99bb..fe2f0f6d 100644 --- a/qse/cmd/http/httpd.c +++ b/qse/cmd/http/httpd.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -171,20 +172,24 @@ static int daemonize (int devnull) /* --------------------------------------------------------------------- */ -enum +enum { - SCFG_NAME, - SCFG_ROOT, - SCFG_REALM, - SCFG_AUTH, - SCFG_DIRCSS, - SCFG_ERRCSS, SCFG_SSLCERTFILE, SCFG_SSLKEYFILE, - SCFG_MAX }; +enum +{ + XCFG_NAME, + XCFG_ROOT, + XCFG_REALM, + XCFG_AUTH, + XCFG_DIRCSS, + XCFG_ERRCSS, + XCFG_MAX +}; + struct cgi_t { enum { @@ -235,6 +240,50 @@ struct access_t struct access_t* next; }; + +typedef struct server_loccfg_t server_loccfg_t; +struct server_loccfg_t +{ + qse_mchar_t* locname; + + qse_mchar_t* xcfg[XCFG_MAX]; + int root_is_nwad; + qse_nwad_t root_nwad; + struct + { + qse_size_t count; + qse_mchar_t* files; + } index; + + struct + { + struct cgi_t* head; + struct cgi_t* tail; + } cgi[CGI_MAX]; + + struct + { + struct mime_t* head; + struct mime_t* tail; + } mime[MIME_MAX]; + + struct + { + struct access_t* head; + struct access_t* tail; + } access[2][ACCESS_MAX]; + + server_loccfg_t* next; +}; + +typedef struct server_hostcfg_t server_hostcfg_t; +struct server_hostcfg_t +{ + qse_mchar_t* hostname; + server_loccfg_t* loccfg; + server_hostcfg_t* next; +}; + typedef struct server_xtn_t server_xtn_t; struct server_xtn_t { @@ -244,21 +293,18 @@ struct server_xtn_t int num; qse_nwad_t bind; int secure; + qse_mchar_t* scfg[SCFG_MAX]; qse_httpd_serverstd_makersrc_t orgmakersrc; qse_httpd_serverstd_freersrc_t orgfreersrc; qse_httpd_serverstd_query_t orgquery; - qse_mchar_t* scfg[SCFG_MAX]; + qse_htb_t cfgtab; + + /* ---------------------------------------------- */ + qse_mchar_t* xcfg[XCFG_MAX]; int root_is_nwad; qse_nwad_t root_nwad; - - struct - { - qse_mchar_t* certfile; - qse_mchar_t* keyfile; - } ssl; - struct { qse_size_t count; @@ -284,7 +330,6 @@ struct server_xtn_t } access[2][ACCESS_MAX]; }; - static int make_resource ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, qse_httpd_rsrc_t* rsrc) @@ -365,6 +410,15 @@ static void clear_server_config (qse_httpd_t* httpd, qse_httpd_server_t* server) } } + for (i = 0; i < QSE_COUNTOF(server_xtn->xcfg); i++) + { + if (server_xtn->xcfg[i]) + { + qse_httpd_freemem (httpd, server_xtn->xcfg[i]); + server_xtn->xcfg[i] = QSE_NULL; + } + } + if (server_xtn->index.files) { qse_httpd_freemem (httpd, server_xtn->index.files); @@ -443,7 +497,7 @@ static int query_server ( switch (code) { case QSE_HTTPD_SERVERSTD_NAME: - *(const qse_mchar_t**)result = server_xtn->scfg[SCFG_NAME]; + *(const qse_mchar_t**)result = server_xtn->xcfg[XCFG_NAME]; return 0; case QSE_HTTPD_SERVERSTD_ROOT: @@ -455,13 +509,13 @@ static int query_server ( else { ((qse_httpd_serverstd_root_t*)result)->type = QSE_HTTPD_SERVERSTD_ROOT_PATH; - ((qse_httpd_serverstd_root_t*)result)->u.path = server_xtn->scfg[SCFG_ROOT]; + ((qse_httpd_serverstd_root_t*)result)->u.path = server_xtn->xcfg[XCFG_ROOT]; } return 0; case QSE_HTTPD_SERVERSTD_REALM: - ((qse_httpd_serverstd_realm_t*)result)->name = server_xtn->scfg[SCFG_REALM]; - ((qse_httpd_serverstd_realm_t*)result)->authreq = (server_xtn->scfg[SCFG_REALM] != QSE_NULL); + ((qse_httpd_serverstd_realm_t*)result)->name = server_xtn->xcfg[XCFG_REALM]; + ((qse_httpd_serverstd_realm_t*)result)->authreq = (server_xtn->xcfg[XCFG_REALM] != QSE_NULL); return 0; case QSE_HTTPD_SERVERSTD_AUTH: @@ -471,9 +525,9 @@ static int query_server ( auth = (qse_httpd_serverstd_auth_t*)result; auth->authok = 0; - if (server_xtn->scfg[SCFG_AUTH]) + if (server_xtn->xcfg[XCFG_AUTH]) { - if (qse_mbsxcmp (auth->key.ptr, auth->key.len, server_xtn->scfg[SCFG_AUTH]) == 0) + if (qse_mbsxcmp (auth->key.ptr, auth->key.len, server_xtn->xcfg[XCFG_AUTH]) == 0) { auth->authok = 1; } @@ -482,11 +536,11 @@ static int query_server ( } case QSE_HTTPD_SERVERSTD_DIRCSS: - *(const qse_mchar_t**)result = server_xtn->scfg[SCFG_DIRCSS]; + *(const qse_mchar_t**)result = server_xtn->xcfg[XCFG_DIRCSS]; return 0; case QSE_HTTPD_SERVERSTD_ERRCSS: - *(const qse_mchar_t**)result = server_xtn->scfg[SCFG_ERRCSS]; + *(const qse_mchar_t**)result = server_xtn->xcfg[XCFG_ERRCSS]; return 0; case QSE_HTTPD_SERVERSTD_INDEX: @@ -597,20 +651,38 @@ static int query_server ( /* --------------------------------------------------------------------- */ -static struct +static struct { const qse_char_t* x; const qse_char_t* y; } scfg_items[] = +{ + { QSE_T("ssl-cert-file"), QSE_T("default.ssl-cert-file") }, + { QSE_T("ssl-key-file"), QSE_T("default.ssl-key-file") } +}; + +static struct +{ + const qse_char_t* x; + const qse_char_t* y; +} xcfg_items[] = { { QSE_T("host['*'].location['/'].name"), QSE_T("default.name") }, { QSE_T("host['*'].location['/'].root"), QSE_T("default.root") }, { QSE_T("host['*'].location['/'].realm"), QSE_T("default.realm") }, { QSE_T("host['*'].location['/'].auth"), QSE_T("default.auth") }, { QSE_T("host['*'].location['/'].dir-css"), QSE_T("default.dir-css") }, - { QSE_T("host['*'].location['/'].error-css"), QSE_T("default.error-css") }, - { QSE_T("ssl-cert-file"), QSE_T("default.ssl-cert-file") }, - { QSE_T("ssl-key-file"), QSE_T("default.ssl-key-file") } + { QSE_T("host['*'].location['/'].error-css"), QSE_T("default.error-css") } +}; + +static struct +{ + const qse_char_t* x; + const qse_char_t* y; +} acc_items[] = +{ + { QSE_T("host['*'].location['/'].dir-access"), QSE_T("default.dir-access") }, + { QSE_T("host['*'].location['/'].file-access"), QSE_T("default.file-access") }, }; static int load_server_config ( @@ -641,6 +713,22 @@ static int load_server_config ( } } + for (i = 0; i < QSE_COUNTOF(xcfg_items); i++) + { + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, xcfg_items[i].x); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, xcfg_items[i].y); + if (pair && pair->val->type == QSE_XLI_STR) + { + server_xtn->xcfg[i] = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); + if (server_xtn->xcfg[i] == QSE_NULL) + { + /*qse_printf (QSE_T("ERROR in copying - %s\n"), qse_httpd_geterrmsg (httpd));*/ + qse_printf (QSE_T("ERROR in copying\n")); + return -1; + } + } + } + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("host['*'].location['/'].index")); if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("default.index")); if (pair && pair->val->type == QSE_XLI_STR) @@ -798,15 +886,6 @@ static int load_server_config ( for (i = 0; i < 2; i++) { - static struct - { - const qse_char_t* x; - const qse_char_t* y; - } acc_items[] = - { - { QSE_T("host['*'].location['/'].dir-access"), QSE_T("default.dir-access") }, - { QSE_T("host['*'].location['/'].file-access"), QSE_T("default.file-access") }, - }; pair = qse_xli_findpairbyname (httpd_xtn->xli, list, acc_items[i].x); if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, acc_items[i].y); @@ -870,14 +949,14 @@ static int load_server_config ( /* perform more sanity check */ /* TODO: support multiple auth entries here and above */ #if 0 - if (qse_mbschr (server_xtn->scfg[SCFG_AUTH], QSE_MT(':')) == QSE_NULL) + if (qse_mbschr (server_xtn->xcfg[XCFG_AUTH], QSE_MT(':')) == QSE_NULL) { - qse_printf (QSE_T("WARNING: no colon in the auth string - [%hs]\n"), server_xtn->scfg[SCFG_AUTH]); + qse_printf (QSE_T("WARNING: no colon in the auth string - [%hs]\n"), server_xtn->xcfg[XCFG_AUTH]); } #endif - if (server_xtn->scfg[SCFG_ROOT] && - qse_mbstonwad (server_xtn->scfg[SCFG_ROOT], &server_xtn->root_nwad) >= 0) + if (server_xtn->xcfg[XCFG_ROOT] && + qse_mbstonwad (server_xtn->xcfg[XCFG_ROOT], &server_xtn->root_nwad) >= 0) { server_xtn->root_is_nwad = 1; } @@ -885,6 +964,460 @@ static int load_server_config ( return 0; } + +static struct +{ + const qse_char_t* x; + const qse_char_t* y; +} loc_xcfg_items[] = +{ + { QSE_T("name"), QSE_T("default.name") }, + { QSE_T("root"), QSE_T("default.root") }, + { QSE_T("realm"), QSE_T("default.realm") }, + { QSE_T("auth"), QSE_T("default.auth") }, + { QSE_T("dir-css"), QSE_T("default.dir-css") }, + { QSE_T("error-css"), QSE_T("default.error-css") } +}; + +static struct +{ + const qse_char_t* x; + const qse_char_t* y; +} loc_acc_items[] = +{ + { QSE_T("dir-access"), QSE_T("default.dir-access") }, + { QSE_T("file-access"), QSE_T("default.file-access") } +}; + +static void free_server_loccfg (qse_httpd_t* httpd, server_loccfg_t* loccfg) +{ + qse_size_t i, j; + + for (i = 0; i < QSE_COUNTOF(loccfg->xcfg); i++) + { + if (loccfg->xcfg[i]) + { + qse_httpd_freemem (httpd, loccfg->xcfg[i]); + loccfg->xcfg[i] = QSE_NULL; + } + } + + if (loccfg->index.files) + { + qse_httpd_freemem (httpd, loccfg->index.files); + loccfg->index.files = QSE_NULL; + loccfg->index.count = 0; + } + + for (i = 0; i < QSE_COUNTOF(loccfg->cgi); i++) + { + struct cgi_t* cgi = loccfg->cgi[i].head; + while (cgi) + { + struct cgi_t* x = cgi; + cgi = x->next; + + if (x->shebang) qse_httpd_freemem (httpd, x->shebang); + if (x->spec) qse_httpd_freemem (httpd, x->spec); + if (x) qse_httpd_freemem (httpd, x); + } + + loccfg->cgi[i].head = QSE_NULL; + loccfg->cgi[i].tail = QSE_NULL; + } + + for (i = 0; i < QSE_COUNTOF(loccfg->mime); i++) + { + struct mime_t* mime = loccfg->mime[i].head; + while (mime) + { + struct mime_t* x = mime; + mime = x->next; + + if (x->spec) qse_httpd_freemem (httpd, x->spec); + if (x->value) qse_httpd_freemem (httpd, x->value); + if (x) qse_httpd_freemem (httpd, x); + } + + loccfg->mime[i].head = QSE_NULL; + loccfg->mime[i].tail = QSE_NULL; + } + + for (j = 0; j < QSE_COUNTOF(loccfg->access); j++) + { + for (i = 0; i < QSE_COUNTOF(loccfg->access[j]); i++) + { + struct access_t* access = loccfg->access[j][i].head; + while (access) + { + struct access_t* x = access; + access = x->next; + + if (x->spec) qse_httpd_freemem (httpd, x->spec); + if (x) qse_httpd_freemem (httpd, x); + } + + loccfg->access[j][i].head = QSE_NULL; + loccfg->access[j][i].tail = QSE_NULL; + } + } + + qse_httpd_freemem (httpd, loccfg->locname); + qse_httpd_freemem (httpd, loccfg); +} + +static void free_server_hostcfg (qse_httpd_t* httpd, server_hostcfg_t* hostcfg) +{ + server_loccfg_t* lc, * cur; + + lc = hostcfg->loccfg; + while (lc) + { + cur = lc; + lc = lc->next; + free_server_loccfg (httpd, cur); + } + qse_httpd_freemem (httpd, hostcfg->hostname); + qse_httpd_freemem (httpd, hostcfg); +} + +static int load_server_loccfg (qse_httpd_t* httpd, qse_xli_list_t* list, server_loccfg_t* cfg) +{ + qse_size_t i; + qse_xli_pair_t* pair; + qse_xli_atom_t* atom; + httpd_xtn_t* httpd_xtn; + + httpd_xtn = qse_httpd_getxtnstd (httpd); + + for (i = 0; i < QSE_COUNTOF(loc_xcfg_items); i++) + { + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, loc_xcfg_items[i].x); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, loc_xcfg_items[i].y); + if (pair && pair->val->type == QSE_XLI_STR) + { + cfg->xcfg[i] = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); + if (cfg->xcfg[i] == QSE_NULL) + { + /*qse_printf (QSE_T("ERROR in copying - %s\n"), qse_httpd_geterrmsg (httpd));*/ + qse_printf (QSE_T("ERROR in copying\n")); + return -1; + } + } + } + + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("host['*'].location['/'].index")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("default.index")); + if (pair && pair->val->type == QSE_XLI_STR) + { + const qse_char_t* tmpptr, * tmpend; + qse_size_t count; + + tmpptr = ((qse_xli_str_t*)pair->val)->ptr; + tmpend = tmpptr + ((qse_xli_str_t*)pair->val)->len; + + for (count = 0; tmpptr < tmpend; count++) tmpptr += qse_strlen (tmpptr) + 1; + + cfg->index.count = count; + cfg->index.files = qse_httpd_strntombsdup ( + httpd, ((qse_xli_str_t*)pair->val)->ptr, ((qse_xli_str_t*)pair->val)->len); + if (cfg->index.files == QSE_NULL) + { + qse_printf (QSE_T("ERROR: in copying index\n")); + return -1; + } + } + + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("host['*'].location['/'].cgi")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("default.cgi")); + if (pair && pair->val->type == QSE_XLI_LIST) + { + /* TODO: more sanity check... this can be done with xli schema... if supported */ + qse_xli_list_t* cgilist = (qse_xli_list_t*)pair->val; + for (atom = cgilist->head; atom; atom = atom->next) + { + if (atom->type != QSE_XLI_PAIR) continue; + + pair = (qse_xli_pair_t*)atom; + if (pair->key && pair->name && + (pair->val->type == QSE_XLI_NIL || pair->val->type == QSE_XLI_STR)) + { + struct cgi_t* cgi; + int type; + + if (qse_strcmp (pair->key, QSE_T("prefix")) == 0) type = CGI_PREFIX; + else if (qse_strcmp (pair->key, QSE_T("suffix")) == 0) type = CGI_SUFFIX; + else if (qse_strcmp (pair->key, QSE_T("name")) == 0) type = CGI_NAME; + else continue; + + cgi = qse_httpd_callocmem (httpd, QSE_SIZEOF(*cgi)); + if (cgi == QSE_NULL) + { + qse_printf (QSE_T("ERROR: memory failure in copying cgi\n")); + return -1; + } + + cgi->type = type; + cgi->spec = qse_httpd_strtombsdup (httpd, pair->name); + if (!cgi->spec) + { + qse_httpd_freemem (httpd, cgi); + qse_printf (QSE_T("ERROR: memory failure in copying cgi\n")); + return -1; + } + if (pair->val->type == QSE_XLI_STR) + { + const qse_char_t* tmpptr, * tmpend; + qse_size_t count; + + tmpptr = ((qse_xli_str_t*)pair->val)->ptr; + tmpend = tmpptr + ((qse_xli_str_t*)pair->val)->len; + + for (count = 0; tmpptr < tmpend; count++) + { + if (count == 0) + { + if (qse_strcmp (tmpptr, QSE_T("nph")) == 0) cgi->nph = 1; + } + else if (count == 1) + { + cgi->shebang = qse_httpd_strtombsdup (httpd, tmpptr); + if (!cgi->shebang) + { + qse_httpd_freemem (httpd, cgi->spec); + qse_httpd_freemem (httpd, cgi); + qse_printf (QSE_T("ERROR: memory failure in copying cgi\n")); + return -1; + } + } + + tmpptr += qse_strlen (tmpptr) + 1; + + /* TODO: more sanity check */ + } + + } + if (cfg->cgi[type].tail) + cfg->cgi[type].tail->next = cgi; + else + cfg->cgi[type].head = cgi; + cfg->cgi[type].tail = cgi; + } + } + } + + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, QSE_T("host['*'].location['/'].mime")); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, QSE_T("default.mime")); + if (pair && pair->val->type == QSE_XLI_LIST) + { + qse_xli_list_t* mimelist = (qse_xli_list_t*)pair->val; + for (atom = mimelist->head; atom; atom = atom->next) + { + if (atom->type != QSE_XLI_PAIR) continue; + + pair = (qse_xli_pair_t*)atom; + if (pair->key && pair->val->type == QSE_XLI_STR) + { + struct mime_t* mime; + int type; + + if (qse_strcmp (pair->key, QSE_T("prefix")) == 0 && pair->name) type = MIME_PREFIX; + else if (qse_strcmp (pair->key, QSE_T("suffix")) == 0 && pair->name) type = MIME_SUFFIX; + else if (qse_strcmp (pair->key, QSE_T("name")) == 0 && pair->name) type = MIME_NAME; + else if (qse_strcmp (pair->key, QSE_T("other")) == 0 && !pair->name) type = MIME_OTHER; + else continue; + + mime = qse_httpd_callocmem (httpd, QSE_SIZEOF(*mime)); + if (mime == QSE_NULL) + { + qse_printf (QSE_T("ERROR: memory failure in copying mime\n")); + return -1; + } + + mime->type = type; + mime->spec = qse_httpd_strtombsdup (httpd, pair->name); + if (!mime->spec) + { + qse_httpd_freemem (httpd, mime); + qse_printf (QSE_T("ERROR: memory failure in copying mime\n")); + return -1; + } + + mime->value = qse_httpd_strtombsdup (httpd, ((qse_xli_str_t*)pair->val)->ptr); + if (!mime->value) + { + qse_httpd_freemem (httpd, mime->spec); + qse_httpd_freemem (httpd, mime); + qse_printf (QSE_T("ERROR: memory failure in copying mime\n")); + return -1; + } + + if (cfg->mime[type].tail) + cfg->mime[type].tail->next = mime; + else + cfg->mime[type].head = mime; + cfg->mime[type].tail = mime; + } + } + } + + for (i = 0; i < 2; i++) + { + + pair = qse_xli_findpairbyname (httpd_xtn->xli, list, loc_acc_items[i].x); + if (!pair) pair = qse_xli_findpairbyname (httpd_xtn->xli, QSE_NULL, loc_acc_items[i].y); + if (pair && pair->val->type == QSE_XLI_LIST) + { + qse_xli_list_t* acclist = (qse_xli_list_t*)pair->val; + for (atom = acclist->head; atom; atom = atom->next) + { + if (atom->type != QSE_XLI_PAIR) continue; + + pair = (qse_xli_pair_t*)atom; + if (pair->key && pair->val->type == QSE_XLI_STR) + { + struct access_t* acc; + const qse_char_t* tmp; + int type, value; + + if (qse_strcmp (pair->key, QSE_T("prefix")) == 0 && pair->name) type = ACCESS_PREFIX; + else if (qse_strcmp (pair->key, QSE_T("suffix")) == 0 && pair->name) type = ACCESS_SUFFIX; + else if (qse_strcmp (pair->key, QSE_T("name")) == 0 && pair->name) type = ACCESS_NAME; + else if (qse_strcmp (pair->key, QSE_T("other")) == 0 && !pair->name) type = ACCESS_OTHER; + else continue; + + tmp = ((qse_xli_str_t*)pair->val)->ptr; + if (qse_strcmp (tmp, QSE_T("noent")) == 0) value = 404; + else if (qse_strcmp (tmp, QSE_T("forbid")) == 0) value = 403; + else if (qse_strcmp (tmp, QSE_T("ok")) == 0) value = 200; + else continue; + /* TODO: more sanity check */ + + acc = qse_httpd_callocmem (httpd, QSE_SIZEOF(*acc)); + if (acc == QSE_NULL) + { + qse_printf (QSE_T("ERROR: memory failure in copying acc\n")); + return -1; + } + + acc->type = type; + if (pair->name) + { + acc->spec = qse_httpd_strtombsdup (httpd, pair->name); + if (!acc->spec) + { + qse_httpd_freemem (httpd, acc); + qse_printf (QSE_T("ERROR: memory failure in copying access\n")); + return -1; + } + } + acc->value = value; + + if (cfg->access[i][type].tail) + cfg->access[i][type].tail->next = acc; + else + cfg->access[i][type].head = acc; + cfg->access[i][type].tail = acc; + } + } + } + } + + /* TODO: support multiple auth entries here and above */ + +#if 0 + /* TODO: perform more sanity check */ + if (qse_mbschr (cfg->xcfg[XCFG_AUTH], QSE_MT(':')) == QSE_NULL) + { + qse_printf (QSE_T("WARNING: no colon in the auth string - [%hs]\n"), cfg->xcfg[XCFG_AUTH]); + } +#endif + + if (cfg->xcfg[XCFG_ROOT] && qse_mbstonwad (cfg->xcfg[XCFG_ROOT], &cfg->root_nwad) >= 0) + { + cfg->root_is_nwad = 1; + } + + return 0; +} + +static int load_server_hostcfg (qse_httpd_t* httpd, qse_httpd_server_t* server, qse_xli_list_t* list) +{ + qse_size_t i, j, host_count, loc_count; + qse_xli_pair_t* host, * loc; + httpd_xtn_t* httpd_xtn; + server_xtn_t* server_xtn; + qse_char_t buf[32]; + server_hostcfg_t* hostcfg; + server_loccfg_t* loccfg; + + httpd_xtn = qse_httpd_getxtnstd (httpd); + server_xtn = qse_httpd_getserverstdxtn (httpd, server); + + host_count = qse_xli_getnumpairsbyname (httpd_xtn->xli, list, QSE_T("host")); + if (host_count <= 0) return 0; /* nothing to load */ + + qse_htb_init (&server_xtn->cfgtab, qse_httpd_getmmgr (httpd), host_count + (host_count / 3) + 1, 70, 0, 0); + + for (i = 0; i < host_count; i++) + { + qse_sprintf (buf, QSE_COUNTOF(buf), QSE_T("host[%d]"), i); + host = qse_xli_findpairbyname (httpd_xtn->xli, list, buf); + if (!host) break; + + if (host->val->type == QSE_XLI_LIST && host->name) + { + loc_count = qse_xli_getnumpairsbyname (httpd_xtn->xli, host->val, QSE_T("location")); + + hostcfg = qse_httpd_callocmem (httpd, QSE_SIZEOF(*hostcfg)); + if (hostcfg == QSE_NULL) return -1; + + for (j = loc_count; j > 0; ) + { + j--; + + qse_sprintf (buf, QSE_COUNTOF(buf), QSE_T("location[%d]"), j); + loc = qse_xli_findpairbyname (httpd_xtn->xli, host->val, buf); + if (!loc) break; + + if (loc->val->type == QSE_XLI_LIST && loc->name) + { + loccfg = qse_httpd_callocmem (httpd, QSE_SIZEOF(*loccfg)); + if (loccfg == QSE_NULL) goto oops; + + hostcfg->loccfg = loccfg; + loccfg->next = hostcfg->loccfg; + + if (load_server_loccfg (httpd, loc, loccfg) <= -1) goto oops; + + loccfg->locname = qse_httpd_strtombsdup (httpd, loc->name); + if (loccfg->locname == QSE_NULL) goto oops; + } + } + + if (hostcfg->loccfg) + { + hostcfg->hostname = qse_httpd_strtombsdup (httpd, host->name); + if (hostcfg->hostname == QSE_NULL) goto oops; + + if (qse_htb_insert (&server_xtn->cfgtab, hostcfg->hostname, 0, hostcfg, 0) == QSE_NULL) goto oops; + } + else + { + /* no location configuration found in the host configuration */ + qse_httpd_freemem (httpd, hostcfg); + hostcfg = QSE_NULL; + } + } + } + + return 0; + +oops: + if (hostcfg) free_server_hostcfg (httpd, hostcfg); + return -1; +} + static qse_httpd_server_t* attach_server (qse_httpd_t* httpd, int num, qse_xli_list_t* list) { qse_httpd_server_dope_t dope; @@ -903,7 +1436,6 @@ static qse_httpd_server_t* attach_server (qse_httpd_t* httpd, int num, qse_xli_l return QSE_NULL; } - qse_memset (&dope, 0, QSE_SIZEOF(dope)); if (qse_strtonwad (((qse_xli_str_t*)pair->val)->ptr, &dope.nwad) <= -1) { @@ -1025,6 +1557,8 @@ static int load_config (qse_httpd_t* httpd) { load_server_config (httpd, server, (qse_xli_list_t*)pair->val); /* TODO: error check */ + load_server_hostcfg (httpd, server, (qse_xli_list_t*)pair->val); + /* TODO: error check */ } } } diff --git a/qse/cmd/http/httpd.conf b/qse/cmd/http/httpd.conf index 65584ebe..2e75a8f0 100644 --- a/qse/cmd/http/httpd.conf +++ b/qse/cmd/http/httpd.conf @@ -62,9 +62,10 @@ server { bind = "0.0.0.0:1999"; ssl = yes; - # openssl genrsa -out key.pem - # openssl req -new -key key.pem -out cert.csr - # openssl req -new -x509 -key key.pem -out cert.pem -days 1095 + # you can create certificate/key files as shown below + # openssl genrsa -out key.pem + # openssl req -new -key key.pem -out cert.csr + # openssl req -new -x509 -key key.pem -out cert.pem -days 1095 ssl-cert-file = "/etc/qse/cert.pem"; ssl-key-file = "/etc/qse/key.pem"; @@ -116,11 +117,8 @@ server { #host "www.google.com" { # location "/" { # root = "/home/www/google"; - # auth { - # realm = "jjjjjjjj"; - # name = "www.google.com"; - # password = "zzzzzzzzzzzzzzzz"; - # } + # realm = "jjjjjjjj"; + # auth = "username:password"; # } #} } diff --git a/qse/include/qse/xli/xli.h b/qse/include/qse/xli/xli.h index 87b55b00..f90f4e36 100644 --- a/qse/include/qse/xli/xli.h +++ b/qse/include/qse/xli/xli.h @@ -501,11 +501,16 @@ QSE_EXPORT qse_xli_pair_t* qse_xli_insertpairwithstr ( QSE_EXPORT qse_xli_pair_t* qse_xli_findpairbyname ( - qse_xli_t* xli, + qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* name ); +QSE_EXPORT qse_size_t qse_xli_getnumpairsbyname ( + qse_xli_t* xli, + const qse_xli_list_t* list, + const qse_char_t* name +); QSE_EXPORT void qse_xli_clear ( qse_xli_t* xli diff --git a/qse/lib/xli/xli.c b/qse/lib/xli/xli.c index d397dc7d..c2715d32 100644 --- a/qse/lib/xli/xli.c +++ b/qse/lib/xli/xli.c @@ -350,6 +350,33 @@ void qse_xli_clear (qse_xli_t* xli) free_list (xli, &xli->root); } +static qse_size_t count_pair_byname ( + qse_xli_t* xli, const qse_xli_list_t* list, + const qse_cstr_t* key, const qse_cstr_t* name) +{ + qse_xli_atom_t* p; + qse_size_t count = 0; + + /* TODO: speed up. no linear search */ + p = list->head; + while (p) + { + if (p->type == QSE_XLI_PAIR) + { + qse_xli_pair_t* pair = (qse_xli_pair_t*)p; + if (qse_strxcmp (key->ptr, key->len, pair->key) == 0) + { + if (name == QSE_NULL || + qse_strxcmp (name->ptr, name->len, pair->name) == 0) count++; + } + } + + p = p->next; + } + + return count; +} + static qse_xli_pair_t* find_pair_byname ( qse_xli_t* xli, const qse_xli_list_t* list, const qse_cstr_t* key, const qse_cstr_t* name) @@ -403,8 +430,7 @@ static qse_xli_pair_t* find_pair_byindex ( return QSE_NULL; } -qse_xli_pair_t* qse_xli_findpairbyname ( - qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* name) +qse_xli_pair_t* qse_xli_findpairbyname (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* name) { const qse_char_t* ptr; qse_cstr_t seg; @@ -417,9 +443,7 @@ qse_xli_pair_t* qse_xli_findpairbyname ( while (1) { seg.ptr = ptr; - while (*ptr != QSE_T('\0') && - *ptr != QSE_T('.') && - *ptr != QSE_T('[')) ptr++; + while (*ptr != QSE_T('\0') && *ptr != QSE_T('.') && *ptr != QSE_T('[')) ptr++; if (ptr == seg.ptr) goto inval; seg.len = ptr - seg.ptr; @@ -530,3 +554,139 @@ noent: return QSE_NULL; } +qse_size_t qse_xli_getnumpairsbyname (qse_xli_t* xli, const qse_xli_list_t* list, const qse_char_t* name) +{ + const qse_char_t* ptr; + qse_cstr_t seg; + qse_xli_list_t* curlist; + qse_xli_pair_t* pair; + + curlist = list? list: &xli->root; + + ptr = name; + while (1) + { + seg.ptr = ptr; + while (*ptr != QSE_T('\0') && *ptr != QSE_T('.') && *ptr != QSE_T('[')) ptr++; + if (ptr == seg.ptr) goto inval; + seg.len = ptr - seg.ptr; + + if (curlist->type != QSE_XLI_LIST) + { + /* check the type of curlist. this check is needed + * because of the unconditional switching at the bottom of the + * this loop. this implementation strategy has been chosen + * to provide the segment name easily. */ + goto noent; + } + + if (*ptr == QSE_T('[')) + { + /* index is specified */ + ptr++; + + if (QSE_ISDIGIT(*ptr)) + { + /* numeric index */ + qse_size_t index = 0, count = 0; + do + { + index = index * 10 + (*ptr++ - QSE_T('0')); + count++; + } + while (QSE_ISDIGIT(*ptr)); + + if (*ptr != QSE_T(']')) goto inval; + + pair = find_pair_byindex (xli, curlist, &seg, index); + if (pair == QSE_NULL) + { + seg.len += count + 2; /* adjustment for error message */ + goto noent; + } + } + else if (QSE_ISALPHA(*ptr)) + { + /* word index */ + qse_cstr_t idx; + + idx.ptr = ptr; + do ptr++; while (QSE_ISALNUM(*ptr) || *ptr == QSE_T('_') || *ptr == QSE_T('-')); + idx.len = ptr - idx.ptr; + + if (*ptr != QSE_T(']')) goto inval; + + pair = find_pair_byname (xli, curlist, &seg, &idx); + if (pair == QSE_NULL) + { + seg.len += idx.len + 2; /* adjustment for error message */ + goto noent; + } + } + else if (*ptr == QSE_T('\'') || *ptr == QSE_T('\"')) + { + qse_cstr_t idx; + qse_char_t cc = *ptr++; + + idx.ptr = ptr; + do ptr++; while (*ptr != cc && *ptr != QSE_T('\0')); + idx.len = ptr - idx.ptr; + + if (*ptr != cc) goto inval; + if (*++ptr != QSE_T(']')) goto inval; + + pair = find_pair_byname (xli, curlist, &seg, &idx); + if (pair == QSE_NULL) + { + seg.len += idx.len + 4; /* adjustment for error message */ + goto noent; + } + } + else goto inval; + + ptr++; /* skip ] */ + + if (*ptr == QSE_T('\0')) + { + /* no more segments */ + return 1; + } + else if (*ptr != QSE_T('.')) goto inval; + } + else + { + pair = find_pair_byname (xli, curlist, &seg, QSE_NULL); + if (pair == QSE_NULL) goto noent; + + if (*ptr == QSE_T('\0')) + { + return count_pair_byname (xli, curlist, &seg, QSE_NULL); + } + else + { + pair = find_pair_byname (xli, curlist, &seg, QSE_NULL); + if (pair == QSE_NULL) goto noent; + } + } + + /* more segments to handle */ + QSE_ASSERT (*ptr == QSE_T('.')); + ptr++; + + /* switch to the value regardless of its type. + * check if it is a list in the beginning of the loop + * just after having gotten the next segment name */ + curlist = (qse_xli_list_t*)pair->val; + } + + return pair; + +inval: + qse_xli_seterrnum (xli, QSE_XLI_EINVAL, QSE_NULL); + return 0; + +noent: + qse_xli_seterrnum (xli, QSE_XLI_ENOENT, &seg); + return 0; +} +