commit 418068d8e58c69ef9abf183ac02e942bf5912883
parent 248e0a1c429fa0ce1f35103765e84175a9c7571e
Author: Christoph Lohmann <20h@r-36.net>
Date: Sat, 2 Apr 2022 22:47:11 +0200
Add new REST calling convention.
Diffstat:
4 files changed, 125 insertions(+), 23 deletions(-)
diff --git a/CGI.md b/CGI.md
@@ -59,6 +59,25 @@ If both ways of input are combined, the variables are set as following:
-> $host = server host
-> $port = server port
+## REST CALLING CONVENTION
+
+There is a special mode in geomyidae to imitate REST calling abilities.
+
+When a user requests some non-existing path, geomyidae will start from
+the base and go up the path directories, until it reaches the first not
+existing directory.
+
+ C: /base/some/dir/that/does/not/exist?some-arguments searchterm
+ -> /base exists
+ -> /some exists
+ -> /dir does not exist
+ -> search for index.cgi or index.dcgi in /base/some
+ -> if not found, display directory content
+ -> if found, call index.cgi or index.dcgi as follows:
+ -> $search = »searchterm«
+ -> $arguments = »/dir/that/does/not/exist?some-arguments«
+ -> $host = server host
+ -> $port = server port
## STANDARD CGI
diff --git a/cgi-examples/rest.dcgi b/cgi-examples/rest.dcgi
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# Simple gopher REST interpretation.
+#
+
+if [ -n "$2" ];
+then
+ case "$2" in
+ /articles*)
+ printf "Article 1\n";
+ printf "Article 2\n";
+ ;;
+ /read*)
+ printf "Read me!\n";
+ ;;
+ /write*)
+ printf "Write me!\n";
+ ;;
+ *)
+ ;;
+ esac
+fi
+
diff --git a/geomyidae.8 b/geomyidae.8
@@ -347,7 +347,7 @@ Both .cgi and .dcgi scripts have the same argument call structure (as seen by ge
where
.Pp
.D1 search = query string (type 7) or Qo Qc (type 0)
-.D1 arguments = string after Qo ? Qc in the path or Qo Qc
+.D1 arguments = string after Qo ? Qc in the path, the remaining path or Qo Qc
.D1 host = server's hostname ("localhost" by default)
.D1 port = server's port ("70" by default)
.Pp
@@ -355,6 +355,9 @@ All terms are tab-separated (per gopher protocol) which can cause some
surprises depending on how a script is written. See the CGI file (included
in the geomyidae source archive) for further elaboration.
.Pp
+For a special REST path case for the arguments, see the CGI file for the
+description.
+.Pp
QUIRK: The original gopher client tried to be too intelligent. It is using
gopher+ when you request some resource. When "search" is just the value "+",
"!", "$" or empty, geomyidae will display a gopher+ redirect instead of invoking the
diff --git a/main.c b/main.c
@@ -65,6 +65,8 @@ char *nocgierr = "3Sorry, execution of the token '%s' was requested, but this "
"\tlocalhost\t70\r\n";
char *notfounderr = "3Sorry, but the requested token '%s' could not be found.\tErr"
"\tlocalhost\t70\r\n";
+char *toolongerr = "3Sorry, but the requested token '%s' is a too long path.\tErr"
+ "\tlocalhost\t70\r\n";
char *htredir = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
" \"DTD/xhtml-transitional.dtd\">\n"
@@ -133,13 +135,16 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
int istls)
{
struct stat dir;
- char recvc[1025], recvb[1025], path[1025], *args = NULL, *sear, *c;
+ char recvc[1025], recvb[1025], path[1025], args[1025], argsc[1025],
+ *sear, *c, *sep, *pathp, *recvbp;
int len = 0, fd, i, maxrecv;
filetype *type;
memset(&dir, 0, sizeof(dir));
memset(recvb, 0, sizeof(recvb));
memset(recvc, 0, sizeof(recvc));
+ memset(args, 0, sizeof(args));
+ memset(argsc, 0, sizeof(argsc));
maxrecv = sizeof(recvb) - 1;
if (rlen > maxrecv || rlen < 0)
@@ -204,9 +209,11 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
* selectors.
*/
- args = strchr(recvb, '?');
- if (args != NULL)
- *args++ = '\0';
+ c = strchr(recvb, '?');
+ if (c != NULL) {
+ *c++ = '\0';
+ snprintf(args, sizeof(args), "%s", c);
+ }
if (recvb[0] == '\0') {
recvb[0] = '/';
@@ -222,31 +229,81 @@ handlerequest(int sock, char *req, int rlen, char *base, char *ohost,
return;
}
- snprintf(path, sizeof(path), "%s%s", base, recvb);
+ if (snprintf(path, sizeof(path), "%s%s", base, recvb) > sizeof(path)) {
+ if (loglvl & ERRORS) {
+ logentry(clienth, clientp, recvc,
+ "path truncation occurred");
+ }
+ dprintf(sock, toolongerr, recvc);
+ return;
+ }
fd = -1;
- if (stat(path, &dir) != -1 && S_ISDIR(dir.st_mode)) {
- for (i = 0; i < sizeof(indexf)/sizeof(indexf[0]); i++) {
- if (strlen(path) + strlen(indexf[i]) >= sizeof(path)) {
+ /*
+ * If path could not be found, do:
+ * 1.) Traverse from base directory one dir by dir.
+ * 2.) If one path element, separated by "/", is not found, stop.
+ * 3.) Prepare new args string:
+ *
+ * $args = $rest_of_path + "?" + $args
+ */
+ if (stat(path, &dir) == -1) {
+ memmove(argsc, args, strlen(args));
+ snprintf(path, sizeof(path), "%s", base);
+ recvbp = recvb + 1;
+ while (recvbp != NULL) {
+ sep = strsep(&recvbp, "/");
+ snprintf(path+strlen(path), sizeof(path)-strlen(path),
+ "/%s", sep);
+ if (stat(path, &dir) == -1) {
+ c = strrchr(path, '/');
+ if (c != NULL) {
+ *c++ = '\0';
+ snprintf(args, sizeof(args),
+ "/%s%s%s%s%s",
+ c,
+ (recvbp != NULL)? "/" : "",
+ (recvbp != NULL)? recvbp : "",
+ (argsc[0] != '\0')? "?" : "",
+ (argsc[0] != '\0')? argsc : ""
+ );
+ }
+ /* path fallthrough */
+ break;
+ }
+ }
+ }
+
+ if (stat(path, &dir) != -1) {
+ if (S_ISDIR(dir.st_mode)) {
+ for (i = 0; i < sizeof(indexf)/sizeof(indexf[0]);
+ i++) {
+ if (strlen(path) + strlen(indexf[i])
+ >= sizeof(path)) {
+ if (loglvl & ERRORS) {
+ logentry(clienth, clientp,
+ recvc,
+ "path truncation occurred");
+ }
+ return;
+ }
+ strncat(path, indexf[i],
+ sizeof(path)-strlen(path)-1);
+ fd = open(path, O_RDONLY);
+ if (fd >= 0)
+ break;
+ path[strlen(path)-strlen(indexf[i])] = '\0';
+ }
+ } else {
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ dprintf(sock, notfounderr, recvc);
if (loglvl & ERRORS) {
logentry(clienth, clientp, recvc,
- "path truncation occurred");
+ strerror(errno));
}
return;
}
- strncat(path, indexf[i], sizeof(path) - strlen(path) - 1);
- fd = open(path, O_RDONLY);
- if (fd >= 0)
- break;
- path[strlen(path)-strlen(indexf[i])] = '\0';
- }
- } else {
- fd = open(path, O_RDONLY);
- if (fd < 0) {
- dprintf(sock, notfounderr, recvc);
- if (loglvl & ERRORS)
- logentry(clienth, clientp, recvc, strerror(errno));
- return;
}
}