URI: 
       Add new REST calling convention. - geomyidae - A small C-based gopherd.
  HTML git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/geomyidae/
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR README
   DIR LICENSE
       ---
   DIR commit 418068d8e58c69ef9abf183ac02e942bf5912883
   DIR parent 248e0a1c429fa0ce1f35103765e84175a9c7571e
  HTML Author: Christoph Lohmann <20h@r-36.net>
       Date:   Sat,  2 Apr 2022 22:47:11 +0200
       
       Add new REST calling convention.
       
       Diffstat:
         M CGI.md                              |      19 +++++++++++++++++++
         A cgi-examples/rest.dcgi              |      23 +++++++++++++++++++++++
         M geomyidae.8                         |       5 ++++-
         M main.c                              |     101 ++++++++++++++++++++++++-------
       
       4 files changed, 125 insertions(+), 23 deletions(-)
       ---
   DIR 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
        
   DIR 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
       +
   DIR 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
   DIR 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;
                        }
                }