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 @@ -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; } }