tAdd new REST calling convention. - geomyidae - A small C-based gopherd. (gopher://bitreich.org/1/scm/geomyidae)
git clone git://r-36.net/geomyidae
Log
Files
Refs
README
LICENSE
---
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:
  CGI.md                              |      19 +++++++++++++++++++
  cgi-examples/rest.dcgi              |      23 +++++++++++++++++++++++
  geomyidae.8                         |       5 ++++-
  main.c                              |     101 ++++++++++++++++++++++++-------

4 files changed, 125 insertions(+), 23 deletions(-)
---
diff --git a/CGI.md b/CGI.md
t@@ -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
t@@ -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
t@@ -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
t@@ -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
t@@ -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 = "\n"
                 "\n"
t@@ -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)
t@@ -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] = '/';
t@@ -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;
                 }
         }