Re: [patch] Gopher DPI

[email protected]
Newsgroups gmane.comp.web.dillo.devel
Message-ID <20121215203119.GB22630@darkstar>
Now search works properly. URL looks like
gopher://gopher.floodgap.com:70/7/v2/vs?query=dillo, but ?query is
replaced with %09 internally. I have not found the way to make
redirect. The patch is ready to be commited and should not break
anything as it is a separate DPI that is only called for gopher URLs.

I will try to implement search without forms by adding input dialogs
as a separate patch in my patch queue on top of it. User experience
will be similar to lynx: you open the page and then browser asks you
for search query.

Also patch for src/IO/dpi.c is attached to make Dpi_parse_token more
robust as I accedently made it segfault during development of gopher
DPI.

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

diff -r a22b1167909e 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 a22b1167909e dpi/gopher.c
--- /dev/null
+++ b/dpi/gopher.c
@@ -0,0 +1,297 @@
+/*
+ * 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(...) fprintf(stderr, "[gopher dpi]: " __VA_ARGS__ )
+
+static Dsh *sh;
+
+static 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, "gopher: getaddrinfo: %s\n", gai_strerror(r));
+      return -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;
+
+   freeaddrinfo(servinfo);
+   return sockfd;
+}
+
+/*
+ * Read and display Menu Entity.
+ */
+static void displaymenu(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\n");
+         continue;
+      }
+      type = line[0];
+      username = line + 1;
+
+      selector = strchr(username, '\t');
+      if (!selector) {
+         MSG("no selector\n");
+         continue;
+      }
+      *selector++ = '\0';
+      host = strchr(selector, '\t');
+      if (!host) {
+         MSG("no host\n");
+         continue;
+      }
+      *host++ = '\0';
+      port = strchr(host, '\t');
+      if (!port) {
+         MSG("no port\n");
+         continue;
+      }
+      *port++ = '\0';
+      p = strpbrk(port, "\r\t");
+      if (p == NULL) {
+         MSG("no CR\n");
+         continue;
+      }
+      *p = '\0';
+
+      username = Escape_html_str(username);
+      if (type == 'i')
+         a_Dpip_dsh_printf(sh, 0, "%s\n", username);
+      else if (type == '3')
+         a_Dpip_dsh_printf(sh, 0, "<b>%s</b>\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_uri_str(str->str, "'");
+         if (type == '7') {
+            a_Dpip_dsh_printf(sh, 0, "</pre><form action='%s'>\n", url);
+            a_Dpip_dsh_printf(sh, 0, "<label for=query>%s</label>\n", username);
+            a_Dpip_dsh_printf(sh, 0, "<input name=query>\n");
+            a_Dpip_dsh_printf(sh, 0, "</form><pre>\n");
+         } else {
+            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, *p;
+   FILE *fp;
+
+   sh = a_Dpip_dsh_new(STDIN_FILENO, STDOUT_FILENO, 1024);
+   if (argc == 2) {
+      /* For debugging */
+      dpip_tag = NULL;
+      cmd = NULL;
+      url = 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;
+   }
+
+   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);
+
+   /* Search forms */
+   p = strstr(url, "?query=");
+   if (p != NULL) {
+      *p++ = '%';
+      *p++ = '0';
+      *p++ = '9';
+      memmove(p, p + 4, strlen(p + 4) + 1);
+   }
+
+   /* Parse URI */
+   if (dStrnAsciiCasecmp(url, "gopher://", 9) != 0) {
+      MSG("URI scheme is not gopher\n");
+      return 1;
+   }
+
+   host = url + 9;
+   port = strpbrk(host, ":/");
+   if (port == NULL) {
+      type = '1';
+      port = "70";
+      selector = "";
+   } else {
+      if (*port == ':') {
+         *port = '\0';
+         port++;
+         path = strchr(port, '/');
+         if (path == NULL) {
+            MSG("no path in URL\n");
+            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;
+   }
+
+   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);
+
+   /* Menu Transaction (Type 1 item) */
+   /* Full-Text Search Transaction (Type 7 item) */
+   if (type == '1' || type == '7')
+      displaymenu(fp);
+   else {
+      size_t n;
+      char buf[256], *mime, *ext;
+
+      mime = "application/octet-stream";
+      ext = strrchr(selector, '.');
+      if(ext)
+         ext++;
+      switch (type) {
+         case '0':
+            /* TextFile */
+            mime = "text/plain";
+            break;
+         case 'h':
+            mime = "text/html";
+            break;
+         case 'g':
+            mime = "image/gif";
+            break;
+         case '5':
+         case '9':
+            if (!ext)
+               break;
+            if (dStrAsciiCasecmp(ext, "zip") == 0)
+               mime = "application/zip";
+            else if (dStrAsciiCasecmp(ext, "gz") == 0)
+               mime = "application/gzip";
+            break;
+         case 'I':
+            if (!ext)
+               break;
+            if (dStrAsciiCasecmp(ext, "jpeg") == 0 ||
+                dStrAsciiCasecmp(ext, "jpg") == 0)
+               mime = "image/jpeg";
+            else if (dStrAsciiCasecmp(ext, "png") == 0)
+               mime = "image/png";
+            else if (dStrAsciiCasecmp(ext, "bmp") == 0)
+               mime = "image/bmp";
+            break;
+      }
+
+      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;
+}
diff -r a22b1167909e dpid/dpidrc.in
--- a/dpid/dpidrc.in
+++ b/dpid/dpidrc.in
@@ -2,5 +2,6 @@
 
 proto.file=file/file.dpi
 proto.ftp=ftp/ftp.filter.dpi
+proto.gopher=gopher/gopher.filter.dpi
 proto.https=https/https.filter.dpi
 proto.data=datauri/datauri.filter.dpi
dpifix.patch (text/plain, 544 B)
# HG changeset patch
# Parent ae4b261729fd0e5a5f92445fde4cfe3aaee4f25a

diff -r ae4b261729fd src/IO/dpi.c
--- a/src/IO/dpi.c
+++ b/src/IO/dpi.c
@@ -226,6 +226,11 @@
    _MSG("Dpi_parse_token: {%s}\n", tag);
 
    cmd = a_Dpip_get_attr_l(Tok, conn->TokSize, "cmd");
+   if (!cmd) {
+      MSG("Dpi_parse_token: no cmd in tag: {%s}\n", tag);
+      dFree(tag);
+      return;
+   }
    if (strcmp(cmd, "send_status_message") == 0) {
       msg = a_Dpip_get_attr_l(Tok, conn->TokSize, "msg");
       a_Chain_fcb(OpSend, conn->InfoRecv, msg, cmd);
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.