| Add new REST calling convention. - geomyidae - A small C-based gopherd. | |
| git clone git://bitreich.org/geomyidae/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfri… | |
| Log | |
| Files | |
| Refs | |
| Tags | |
| README | |
| LICENSE | |
| --- | |
| commit 418068d8e58c69ef9abf183ac02e942bf5912883 | |
| parent 248e0a1c429fa0ce1f35103765e84175a9c7571e | |
| Author: Christoph Lohmann <[email protected]> | |
| 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(-) | |
| --- | |
| diff --git a/CGI.md b/CGI.md | |
| @@ -59,6 +59,25 @@ If both ways of input are combined, the variables are set as… | |
| -> $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 str… | |
| 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… | |
| 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 invoki… | |
| diff --git a/main.c b/main.c | |
| @@ -65,6 +65,8 @@ char *nocgierr = "3Sorry, execution of the token '%s' was req… | |
| "\tlocalhost\t70\r\n"; | |
| char *notfounderr = "3Sorry, but the requested token '%s' could not be found.\… | |
| "\tlocalhost\t70\r\n"; | |
| +char *toolongerr = "3Sorry, but the requested token '%s' is a too long path.\t… | |
| + "\tlocalhost\t70\r\n"; | |
| char *htredir = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
| "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//E… | |
| " \"DTD/xhtml-transitional.dtd\">\n" | |
| @@ -133,13 +135,16 @@ handlerequest(int sock, char *req, int rlen, char *base, … | |
| 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, c… | |
| * 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, … | |
| 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 occur… | |
| + } | |
| + 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) -… | |
| - 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(err… | |
| - return; | |
| } | |
| } | |