diff --git a/fcgi.c b/fcgi.c index e681e1b..2219ecb 100644 --- a/fcgi.c +++ b/fcgi.c @@ -371,12 +371,37 @@ fcgi_error(struct bufferevent *bev, short err, void *d) c->type = REQUEST_DONE; } +static void +path_translate(const char *path, struct location *loc, struct location *rloc, + char *buf, size_t len) +{ + const char *root, *sufx; + + buf[0] = '\0'; + + if (*loc->dir != '\0') + root = loc->dir; + else if (*rloc->dir != '\0') + root = rloc->dir; + else + return; + + sufx = ""; + if (*root != '\0') + sufx = root[strlen(root) - 1] == '/' ? "" : "/"; + + while (*path == '/') + path++; + + snprintf(buf, len, "%s%s%s", root, sufx, path); +} + void fcgi_req(struct client *c, struct location *loc) { - char buf[22], path[GEMINI_URL_LEN]; + char buf[22], path[GEMINI_URL_LEN], path_tr[PATH_MAX]; char *scriptname, *qs; - const char *stripped; + const char *stripped, *port; size_t l; time_t tim; struct tm tminfo; @@ -384,6 +409,16 @@ fcgi_req(struct client *c, struct location *loc) fcgi_begin_request(c->cgibev); + stripped = strip_path(c->iri.path, loc->fcgi_strip); + if (*stripped != '/') + snprintf(path, sizeof(path), "/%s", stripped); + else + strlcpy(path, stripped, sizeof(path)); + + port = c->iri.host; + if (port == NULL || *port == '\0') + port = "1965"; + scriptname = ""; TAILQ_FOREACH(p, &loc->params, envs) { if (!strcmp(p->name, "SCRIPT_NAME")) { @@ -392,35 +427,36 @@ fcgi_req(struct client *c, struct location *loc) } } - stripped = strip_path(c->iri.path, loc->fcgi_strip); - if (*stripped != '/') - snprintf(path, sizeof(path), "/%s", stripped); - else - strlcpy(path, stripped, sizeof(path)); - l = strlen(scriptname); while (l > 0 && scriptname[l - 1] == '/') l--; if (!strncmp(scriptname, path, l) && (path[l] == '/' || path[l] == '\0')) { fcgi_send_param(c->cgibev, "PATH_INFO", &path[l]); + path_translate(&path[l], loc, TAILQ_FIRST(&c->host->locations), + path_tr, sizeof(path_tr)); path[l] = '\0'; fcgi_send_param(c->cgibev, "SCRIPT_NAME", path); } else { + path_translate(stripped, loc, TAILQ_FIRST(&c->host->locations), + path_tr, sizeof(path_tr)); fcgi_send_param(c->cgibev, "PATH_INFO", stripped); fcgi_send_param(c->cgibev, "SCRIPT_NAME", scriptname); } fcgi_send_param(c->cgibev, "GATEWAY_INTERFACE", "CGI/1.1"); - fcgi_send_param(c->cgibev, "GEMINI_URL_PATH", c->iri.path); + fcgi_send_param(c->cgibev, "PATH_TRANSLATED", path_tr); fcgi_send_param(c->cgibev, "QUERY_STRING", c->iri.query); fcgi_send_param(c->cgibev, "REMOTE_ADDR", c->rhost); fcgi_send_param(c->cgibev, "REMOTE_HOST", c->rhost); - fcgi_send_param(c->cgibev, "REQUEST_METHOD", ""); + fcgi_send_param(c->cgibev, "REQUEST_METHOD", "GET"); fcgi_send_param(c->cgibev, "SERVER_NAME", c->iri.host); + fcgi_send_param(c->cgibev, "SERVER_PORT", port); fcgi_send_param(c->cgibev, "SERVER_PROTOCOL", "GEMINI"); fcgi_send_param(c->cgibev, "SERVER_SOFTWARE", GMID_VERSION); + fcgi_send_param(c->cgibev, "GEMINI_URL_PATH", c->iri.path); + if (*c->iri.query != '\0' && strchr(c->iri.query, '=') == NULL && (qs = strdup(c->iri.query)) != NULL) { diff --git a/gmid.conf.5 b/gmid.conf.5 index 5738e72..fc78452 100644 --- a/gmid.conf.5 +++ b/gmid.conf.5 @@ -11,7 +11,7 @@ .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.Dd July 24, 2023 +.Dd August 18, 2023 .Dt GMID.CONF 5 .Os .Sh NAME @@ -326,25 +326,25 @@ leading path components from the .Pp The FastCGI handler will be given the following variables by default: .Bl -tag -width 24m -.It Ev GATEWAY_INTERFACE -.Dq CGI/1.1 -.It Ev GEMINI_DOCUMENT_ROOT -The root directory of the virtual host. -.It Ev GEMINI_SCRIPT_FILENAME -Full path to the FastCGI script being executed. -.It Ev GEMINI_URL -The full IRI of the request. +.\" .It Ev GEMINI_DOCUMENT_ROOT +.\" The root directory of the virtual host. .It Ev GEMINI_URL_PATH -The path of the request. +Full path of the request. .It Ev GEMINI_SEARCH_STRING The decoded .Ev QUERY_STRING if defined in the request and if it doesn't contain any unencoded .Sq = characters, otherwise unset. +.It Ev GATEWAY_INTERFACE +.Dq CGI/1.1 +.It Ev AUTH_TYPE +The string "Certificate" if the client used a certificate, otherwise +unset. .It Ev PATH_INFO The portion of the requested path that is derived from the the IRI -path hierarchy following the part that identifies the script itself. +path hierarchy following +.Ev SCRIPT_NAME . Can be unset. .It Ev PATH_TRANSLATED Present if and only if @@ -362,9 +362,17 @@ The URL-encoded search or parameter string. Textual representation of the client IP. .It Ev REQUEST_METHOD This is present only for RFC3875 (CGI) compliance. -It's always set to the empty string. +It's always set to +.Dq GET . .It Ev SCRIPT_NAME The virtual URI path to the script. +Since it's impossible to determine in all cases the correct +.Ev SCRIPT_NAME +programmatically +.Nm gmid +assumes it's the empty string. +It is recommended to manually specify this parameter when serving a +sub-tree of a virtual host via FastCGI. .It Ev SERVER_NAME The name of the server .It Ev SERVER_PORT @@ -374,9 +382,6 @@ The port the server is listening on. .It Ev SERVER_SOFTWARE The name and version of the server, i.e. .Dq gmid/1.8.4 -.It Ev AUTH_TYPE -The string "Certificate" if the client used a certificate, otherwise -unset. .It Ev REMOTE_USER The subject of the client certificate if provided, otherwise unset. .It Ev TLS_CLIENT_ISSUER