[patch] Gopher DPI

[email protected]
Newsgroups gmane.comp.web.dillo.devel
Message-ID <20121215152423.GA9636@darkstar>
I implemented basic Gopher support using DPI, patch is attached. It is
already quite usable.

"URL:" links are not supported, but Gopher servers send redirect pages
for compatibility.

The only major problem is unrecognized file types. I looked at FTP
DPI. It sends Content-type: application/octet-stream. This forces
Dillo to ask downloads DPI to start download. With gopher it doesn't
work as downloads DPI doesn't suppport gopher.

So Dillo just says "Content-Type 'application/octet-stream' not
viewable.". I left it as it is for now, because I don't think adding
support for all known and unknown protocols into downloads.cc is the
right solution. Maybe downloads DPI protocol should be changed so
Dillo would feed plugin with data it receives as octet-stream and
downloads DPI would only save what it receives to disk instead of
connecting to server and downloading the file itself.

_______________________________________________
Dillo-dev mailing list
[email protected]
http://lists.auriga.wearlab.de/cgi-bin/mailman/listinfo/dillo-dev
gopher.patch (text/plain, 7.5 KB)
# HG changeset patch
# Parent 8d38d50f877390aebcc631402fe8102b8e52e4bc

diff -r 8d38d50f8773 dpi/Makefile.am
--- a/dpi/Makefile.am
+++ b/dpi/Makefile.am
@@ -4,6 +4,7 @@
 bookmarksdir = $(libdir)/dillo/dpi/bookmarks
 downloadsdir = $(libdir)/dillo/dpi/downloads
 ftpdir = $(libdir)/dillo/dpi/ftp
+gopherdir = $(libdir)/dillo/dpi/gopher
 httpsdir = $(libdir)/dillo/dpi/https
 hellodir = $(libdir)/dillo/dpi/hello
 vsourcedir = $(libdir)/dillo/dpi/vsource
@@ -13,6 +14,7 @@
 bookmarks_PROGRAMS = bookmarks.dpi
 downloads_PROGRAMS = downloads.dpi
 ftp_PROGRAMS = ftp.filter.dpi
+gopher_PROGRAMS = gopher.filter.dpi
 https_PROGRAMS = https.filter.dpi
 hello_PROGRAMS = hello.filter.dpi
 vsource_PROGRAMS = vsource.filter.dpi
@@ -29,6 +31,9 @@
 ftp_filter_dpi_LDADD = \
 	$(top_builddir)/dpip/libDpip.a \
 	$(top_builddir)/dlib/libDlib.a
+gopher_filter_dpi_LDADD = \
+	$(top_builddir)/dpip/libDpip.a \
+	$(top_builddir)/dlib/libDlib.a
 https_filter_dpi_LDADD = @LIBSSL_LIBS@ \
 	$(top_builddir)/dpip/libDpip.a \
 	$(top_builddir)/dlib/libDlib.a
@@ -53,6 +58,7 @@
 bookmarks_dpi_SOURCES = bookmarks.c dpiutil.c dpiutil.h
 downloads_dpi_SOURCES = downloads.cc dpiutil.c dpiutil.h
 ftp_filter_dpi_SOURCES = ftp.c dpiutil.c dpiutil.h
+gopher_filter_dpi_SOURCES = gopher.c dpiutil.c dpiutil.h
 https_filter_dpi_SOURCES = https.c dpiutil.c dpiutil.h
 hello_filter_dpi_SOURCES = hello.c dpiutil.c dpiutil.h
 vsource_filter_dpi_SOURCES = vsource.c dpiutil.c dpiutil.h
diff -r 8d38d50f8773 dpi/gopher.c
--- /dev/null
+++ b/dpi/gopher.c
@@ -0,0 +1,255 @@
+/*
+ * DPI for Gopher.
+ *
+ * Standards:
+ * https://tools.ietf.org/html/rfc1436 - The Internet Gopher Protocol
+ * https://tools.ietf.org/html/rfc4266 - The gopher URI Scheme
+ * http://gopher.quux.org:70/Archives/Mailing%20Lists/gopher/gopher.2002-02|/MBOX-MESSAGE/34
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "../dpip/dpip.h"
+#include "dpiutil.h"
+
+#define _MSG(...)
+#define MSG(...)  printf("[gopher dpi]: " __VA_ARGS__)
+
+static Dsh *sh;
+
+int connectto(char *host, char *port) {
+   struct addrinfo hints, *servinfo, *p;
+   int sockfd, r;
+
+   memset(&hints, 0, sizeof hints);
+   hints.ai_family = AF_UNSPEC;
+   hints.ai_socktype = SOCK_STREAM;
+
+   r = getaddrinfo(host, port, &hints, &servinfo);
+   if (r != 0) {
+      fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(r));
+      exit(1);
+   }
+
+   for(p = servinfo; p != NULL; p = p->ai_next) {
+      if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1) {
+         perror("gopher: socket");
+         continue;
+      }
+
+      if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
+         close(sockfd);
+         perror("gopher: connect");
+         continue;
+      }
+
+      break;
+   }
+
+   if (p == NULL) {
+      return -1;
+      exit(1);
+   }
+
+   freeaddrinfo(servinfo);
+   return sockfd;
+}
+
+void displaydirectory(FILE *fp) {
+   ssize_t n;
+   size_t len;
+   char *line, type, *username, *selector, *host, *port, *p;
+
+   a_Dpip_dsh_write_str(sh, 0,
+      "Content-type: text/html\n\n"
+      "<!doctype html>\n"
+      "<title>Gopher</title>\n"
+      "<pre>\n");
+
+   line = NULL;
+   while ((n = getline(&line, &len, fp)) != -1) {
+      if (len == 0) {
+         MSG("no type");
+         continue;
+      }
+      type = line[0];
+      username = line + 1;
+
+      selector = strchr(username, '\t');
+      if (!selector) {
+         MSG("no selector");
+         continue;
+      }
+      *selector++ = '\0';
+      host = strchr(selector, '\t');
+      if (!host) {
+         MSG("no host");
+         continue;
+      }
+      *host++ = '\0';
+      port = strchr(host, '\t');
+      if (!port) {
+         MSG("no port");
+         continue;
+      }
+      *port++ = '\0';
+      p = strpbrk(port, "\r\t");
+      if (p == NULL) {
+         MSG("no CR");
+         continue;
+      }
+      *p = '\0';
+  
+      username = Escape_html_str(username);
+      if (type == 'i')
+         a_Dpip_dsh_printf(sh, 0, "%s\n", username);
+      else {
+         Dstr *str;
+         char *url;
+
+         str = dStr_new(NULL);
+         dStr_sprintf(str, "gopher://%s:%s/%c%s", host, port, type, selector, username);
+         url = Escape_html_str(str->str);
+         a_Dpip_dsh_printf(sh, 0, "<a href='%s'>%s</a>\n", url, username);
+         dFree(url);
+         dStr_free(str, 1);
+      }
+      dFree(username);
+   }
+   free(line);
+}
+
+int main(int argc, char *argv[]) {
+   char *dpip_tag, *cmd, *url, *d_cmd;
+   int sockfd;
+   char *host, *port, type, *path, *selector;
+   FILE *fp;
+   
+   sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 1024);
+
+   if (argc == 2) {
+      dpip_tag = dStrdup(argv[1]);
+   } else {
+      /* Authenticate client. */
+      dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+      if (!dpip_tag || a_Dpip_check_auth(dpip_tag) < 0) {
+         MSG("can't authenticate request: %s\n", dStrerror(errno));
+         a_Dpip_dsh_close(sh);
+         return 1;
+      }
+      dFree(dpip_tag);
+      /* Read the DPI command. */
+      dpip_tag = a_Dpip_dsh_read_token(sh, 1);
+   }
+
+   cmd = a_Dpip_get_attr(dpip_tag, "cmd");
+   url = a_Dpip_get_attr(dpip_tag, "url");
+   if (!cmd || !url)
+      return 1;
+
+   /* Parse URI */
+   if (dStrnAsciiCasecmp(url, "gopher://", 9) != 0) {
+      MSG("URI scheme is not gopher");
+      return 1;
+   }
+
+   host = url + 9;
+   port = strpbrk(host, ":/");
+   if (port == NULL) {
+      MSG("wrong URI");
+      return 1;
+   }
+
+   if (*port == ':') {
+      *port = '\0';
+      port++;
+      path = strchr(port, '/');
+      if (path == NULL) {
+         MSG("no path in URL");
+         return 1;
+      }
+      *path = '\0';
+      path++;
+   } else {
+      *port = '\0';
+      path = port + 1;
+      port = "70";
+   }
+   type = path[0];
+   if (type == '\0') {
+      type = '1'; /* RFC 4266 says <gophertype> defaults to 1 */
+      selector = "";
+   } else
+      selector = path + 1;
+
+   /* Connect to gopher server */
+   MSG("host: %s, port: %s\n", host, port);
+   sockfd = connectto(host, port);
+   if(sockfd == -1) {
+      MSG("failed to connect\n");
+      return 1;
+   }
+
+   d_cmd = a_Dpip_build_cmd("cmd=%s url=%s", "start_send_page", url);
+   a_Dpip_dsh_write_str(sh, 0, d_cmd);
+   dFree(d_cmd);
+
+   fp = fdopen(sockfd, "rw+");
+   if (!fp) {
+      MSG("fdopen\n");
+      return 1;
+   }
+
+   selector = Unescape_uri_str(selector);
+   MSG("sending selector: %s\n", selector);
+   fprintf(fp, "%s\r\n", selector);
+   if (type == '1')
+      displaydirectory(fp);
+   else {
+      size_t n;
+      char buf[256], *mime, *ext;
+
+      mime = "application/octet-stream";
+      ext = strrchr(selector, '.');
+      switch (type) {
+         case '0':
+            mime = "text/plain";
+            break;
+         case 'h':
+            mime = "text/html";
+            break;
+         case 'g':
+            mime = "image/gif";
+            break;
+         case 'I':
+            if (!ext)
+               break;
+            if (strcasecmp(ext, ".jpeg") == 0 || strcasecmp(ext, ".jpg") == 0)
+               mime = "image/jpeg";
+            else if (strcasecmp(ext, ".png") == 0)
+               mime = "image/png";
+      }
+
+      a_Dpip_dsh_printf(sh, 1, "Content-type: %s\r\n\r\n", mime);
+      while ((n = fread (buf, 1, sizeof buf, fp)) > 0) {
+         a_Dpip_dsh_write(sh, 1, buf, n);
+      }
+   }
+
+   dFree(selector);
+   dFree(cmd);
+   dFree(url);
+   dFree(dpip_tag);
+
+   a_Dpip_dsh_close(sh);
+   a_Dpip_dsh_free(sh);
+   return 0;
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.