[patch] Gopher DPI
| 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;
+}