Commit Diff


commit - a13739138b17a21dbb50011cc65fb135e9f804c8
commit + 9f006a2127398af12ecf9159cd5ef28b3685e7a6
blob - bf662e37bc7aecfb660fa7d68f1c2d5885361f33
blob + 187a4ac461eb025869e5586e5aecd819b4ee8344
--- ChangeLog
+++ ChangeLog
@@ -1,5 +1,7 @@
 2021-02-07  Omar Polo  <op@omarpolo.com>
 
+	* ex.c (do_exec): [cgi] split the query in words if needed and add them to the argv
+
 	* parse.y (option): added prefork option
 
 2021-02-06  Omar Polo  <op@omarpolo.com>
blob - 58f5004c7513d3f6f2e4566099fad2bb3b9cb1c4
blob + 0a65472deabc66510a2c758b4d3ac50ecb837912
--- ex.c
+++ ex.c
@@ -228,6 +228,46 @@ xasprintf(const char *fmt, ...)
 	va_end(ap);
 
 	return s;
+}
+
+static void
+do_exec(const char *ex, const char *spath, char *query)
+{
+	char **argv, buf[PATH_MAX], *sname, *t;
+	size_t i, n;
+
+	strlcpy(buf, spath, sizeof(buf));
+	sname = basename(buf);
+
+	if (query == NULL || strchr(query, '=') != NULL) {
+		if ((argv = calloc(2, sizeof(char*))) == NULL)
+			err(1, "calloc");
+		argv[0] = sname;
+		execvp(ex, argv);
+		warn("execvp: %s", argv[0]);
+		return;
+	}
+
+	n = 1;
+	for (t = query ;; t++, n++) {
+		if ((t = strchr(t, '+')) == NULL)
+			break;
+	}
+
+	if ((argv = calloc(n+2, sizeof(char*))) == NULL)
+		err(1, "calloc");
+
+	argv[0] = sname;
+	for (i = 0; i < n; ++i) {
+		t = strchr(query, '+');
+		if (t != NULL)
+			*t = '\0';
+		argv[i+1] = pct_decode_str(query);
+		query = t+1;
+	}
+
+	execvp(ex, argv);
+	warn("execvp: %s", argv[0]);
 }
 
 /* fd or -1 on error */
@@ -246,7 +286,6 @@ launch_cgi(struct iri *iri, const char *spath, char *r
 		return -1;
 
 	case 0: {		/* child */
-		char *argv[] = {NULL, NULL};
 		char *ex, *pwd;
 		char iribuf[GEMINI_URL_LEN];
 		char path[PATH_MAX];
@@ -256,7 +295,6 @@ launch_cgi(struct iri *iri, const char *spath, char *r
 			goto childerr;
 
 		ex = xasprintf("%s/%s", vhost->dir, spath);
-		argv[0] = ex;
 
 		serialize_iri(iri, iribuf, sizeof(iribuf));
 
@@ -307,15 +345,15 @@ launch_cgi(struct iri *iri, const char *spath, char *r
 		safe_setenv("TLS_CLIENT_ISSUER", cissuer);
 		safe_setenv("TLS_CLIENT_HASH", chash);
 
-		strlcpy(path, argv[0], sizeof(path));
+		strlcpy(path, ex, sizeof(path));
+
 		pwd = dirname(path);
 		if (chdir(pwd)) {
 			warn("chdir");
 			goto childerr;
 		}
 
-		execvp(argv[0], argv);
-		warn("execvp: %s", argv[0]);
+		do_exec(ex, spath, iri->query);
 		goto childerr;
 	}
 
blob - af829ba968a9fc8b2472ce6efd7403d4811d37ef
blob + 1beb95e383fae7773dede8dc27991e251dc5e9d7
--- gmid.h
+++ gmid.h
@@ -257,6 +257,7 @@ char		*utf8_nth(char*, size_t);
 int		 parse_iri(char*, struct iri*, const char**);
 int		 trim_req_iri(char*, const char **);
 int		 serialize_iri(struct iri*, char*, size_t);
+char		*pct_decode_str(char *);
 
 /* puny.c */
 int		 puny_decode(const char*, char*, size_t, const char**);
blob - 1d550efe97334463553ecebb6f9d4392cc0a1954
blob + b911b0dd6408e147ca0a6beb908dfc8d5ef53d9c
--- iri.c
+++ iri.c
@@ -75,6 +75,13 @@ valid_pct_encoded(struct parser *p)
 	return 1;
 }
 
+static void
+pct_decode(char *s)
+{
+	sscanf(s+1, "%2hhx", s);
+	memmove(s+1, s+3, strlen(s+3)+1);
+}
+
 static int
 parse_pct_encoded(struct parser *p)
 {
@@ -86,8 +93,7 @@ parse_pct_encoded(struct parser *p)
 		return 0;
 	}
 
-	sscanf(p->iri+1, "%2hhx", p->iri);
-	memmove(p->iri+1, p->iri+3, strlen(p->iri+3)+1);
+	pct_decode(p->iri);
 	if (*p->iri == '\0') {
 		p->err = "illegal percent-encoding";
 		return 0;
@@ -437,3 +443,16 @@ serialize_iri(struct iri *i, char *buf, size_t len)
 
 	return l < len;
 }
+
+char *
+pct_decode_str(char *s)
+{
+	char *t;
+
+	for (t = s; *t; ++t) {
+		if (*t == '%' && valid_pct_enc_string(t))
+			pct_decode(t);
+	}
+
+	return s;
+}
blob - 9492b1d92e0ab68cca7f4ac7cd36fb69d7c7cfe9
blob + 1c991b3530bd7f9d63f0b5bedc5690ea41de564a
--- regress/runtime
+++ regress/runtime
@@ -69,6 +69,10 @@ quit() {
 	wait || true
 }
 
+count() {
+	wc -l | xargs
+}
+
 # usage: eq a b errmsg
 # if a and b aren't equal strings, exit with errmsg
 eq() {
@@ -152,7 +156,7 @@ echo OK GET / with custom lang
 
 check "should be running"
 
-# finally try with CGI scripts
+# try with CGI scripts
 config '' 'cgi "*"'
 checkconf
 restart
@@ -179,6 +183,13 @@ get /bigfile > bigfile
 eq "$(cat bigfile.sha)"	"$(cat testdata/bigfile.sha)" "Unexpected sha for /serve-bigfile"
 echo OK GET /serve-bigfile with cgi
 
+# ensure we split the query correctly
+eq "$(get /env | awk /^-/ | count)"		1	"Unexpected number of arguments"
+eq "$(get /env?foo | awk /^-/ | count)"		2	"Unexpected number of arguments"
+eq "$(get /env?foo+bar | awk /^-/ | count)"	3	"Unexpected number of arguments"
+eq "$(get /env?foo+bar=5 | awk /^-/ | count)"	1	"Unexpected number of arguments"
+eq "$(get /env?foo+bar%3d5 | awk /^-/ | count)"	3	"Unexpected number of arguments"
+
 check "should be running"
 
 config '' 'index "foo.gmi"'