diff --git a/Makefile.config.in b/Makefile.config.in index 583d8b15..c83fccf7 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -114,6 +114,7 @@ CONFIG_EXMODE = @CONFIG_EXMODE@ CONFIG_FASTMEM = @CONFIG_FASTMEM@ CONFIG_FINGER = @CONFIG_FINGER@ CONFIG_FORMHIST = @CONFIG_FORMHIST@ +CONFIG_FSP = @CONFIG_FSP@ CONFIG_FTP = @CONFIG_FTP@ CONFIG_GLOBHIST = @CONFIG_GLOBHIST@ CONFIG_GNUTLS = @CONFIG_GNUTLS@ diff --git a/configure.in b/configure.in index e70b85e4..80bb9d00 100644 --- a/configure.in +++ b/configure.in @@ -1132,6 +1132,24 @@ EL_ARG_DEPEND(CONFIG_CGI, cgi, [HAVE_SETENV_OR_PUTENV:yes], [Local CGI], EL_ARG_ENABLE(CONFIG_FINGER, finger, [Finger protocol], [ --enable-finger enable finger protocol support]) +dnl =================================================================== +dnl FSP protocol +dnl =================================================================== + +EL_ARG_ENABLE(CONFIG_FSP, fsp, [FSP protocol], + [ --enable-fsp enable FSP protocol support]) + +if test "x${enable_fsp}" != xno; then + AC_CHECK_HEADERS(fsplib.h, CONFIG_FSP=yes, CONFIG_FSP=no) + + if test "$CONFIG_FSP" = yes; then + AC_CHECK_LIB(fsplib, fsp_open_session, CONFIG_FSP=yes, CONFIG_FSP=no) + if test "$CONFIG_FSP" = yes; then + LIBS="$LIBS -lfsplib" + fi + fi +fi + EL_ARG_ENABLE(CONFIG_FTP, ftp, [FTP protocol], [ --disable-ftp disable ftp protocol support]) diff --git a/features.conf b/features.conf index e725316b..c19ce563 100644 --- a/features.conf +++ b/features.conf @@ -297,6 +297,13 @@ CONFIG_DATA=yes CONFIG_FINGER=no +### FSP protocol +# +# See: http://fsp.sourceforge.net/ +# +# Default: disabled + +CONFIG_FSP=no ### File Transfer Protocol Support # diff --git a/src/protocol/Makefile b/src/protocol/Makefile index 9c49a21c..f4e83fdb 100644 --- a/src/protocol/Makefile +++ b/src/protocol/Makefile @@ -3,6 +3,7 @@ include $(top_builddir)/Makefile.config SUBDIRS-$(CONFIG_BITTORRENT) += bittorrent SUBDIRS-$(CONFIG_FINGER) += finger +SUBDIRS-$(CONFIG_FSP) += fsp SUBDIRS-$(CONFIG_FTP) += ftp SUBDIRS-$(CONFIG_GOPHER) += gopher SUBDIRS-$(CONFIG_NNTP) += nntp diff --git a/src/protocol/fsp/Makefile b/src/protocol/fsp/Makefile new file mode 100644 index 00000000..87341f6e --- /dev/null +++ b/src/protocol/fsp/Makefile @@ -0,0 +1,6 @@ +top_builddir=../../.. +include $(top_builddir)/Makefile.config + +OBJS = fsp.o + +include $(top_srcdir)/Makefile.lib diff --git a/src/protocol/fsp/fsp.c b/src/protocol/fsp/fsp.c new file mode 100644 index 00000000..6f1d3d3c --- /dev/null +++ b/src/protocol/fsp/fsp.c @@ -0,0 +1,341 @@ +/* Internal FSP protocol implementation */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* Needed for asprintf() */ +#endif + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include /* FreeBSD needs this before resource.h */ +#endif +#include /* FreeBSD needs this before resource.h */ +#ifdef HAVE_SYS_RESOURCE_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include /* OS/2 needs this after sys/types.h */ +#endif +#ifdef HAVE_UNISTD_H +#include +#endif + +#include "elinks.h" + +#include "cache/cache.h" +#include "config/options.h" +#include "intl/gettext/libintl.h" +#include "main/module.h" +#include "main/select.h" +#include "network/connection.h" +#include "network/socket.h" +#include "osdep/osdep.h" +#include "protocol/protocol.h" +#include "protocol/fsp/fsp.h" +#include "protocol/uri.h" +#include "util/memory.h" +#include "util/snprintf.h" +#include "util/string.h" + + +struct option_info fsp_options[] = { + INIT_OPT_TREE("protocol", N_("FSP"), + "fsp", 0, + N_("FSP specific options.")), + + INIT_OPT_BOOL("protocol.fsp", N_("Sort entries"), + "sort", 0, 1, + N_("Whether sort entries in directory listings.")), + + NULL_OPTION_INFO, +}; + +struct module fsp_protocol_module = struct_module( + /* name: */ N_("FSP"), + /* options: */ fsp_options, + /* hooks: */ NULL, + /* submodules: */ NULL, + /* data: */ NULL, + /* init: */ NULL, + /* done: */ NULL +); + + +struct fsp_info { + int init; +}; + +static void +fsp_error(unsigned char *error) +{ + puts(error); + exit(1); +} + +static int +compare(FSP_RDENTRY *a, FSP_RDENTRY *b) +{ + int res = ((b->type == FSP_RDTYPE_DIR) - (a->type == FSP_RDTYPE_DIR)); + + if (res) + return res; + return strcmp(a->name, b->name); +} + + +static void +sort_and_display_entries(FSP_DIR *dir) +{ + FSP_RDENTRY fentry, *fresult, *table = NULL; + int size = 0; + int i; + unsigned char dircolor[8]; + + if (get_opt_bool("document.browse.links.color_dirs")) { + color_to_string(get_opt_color("document.colors.dirs"), + (unsigned char *) &dircolor); + } else { + dircolor[0] = 0; + } + + while (!fsp_readdir_native(dir, &fentry, &fresult)) { + FSP_RDENTRY *new_table; + + if (!fresult) break; + if (!strcmp(fentry.name, ".")) + continue; + new_table = mem_realloc(table, (size + 1) * sizeof(*table)); + if (!new_table) + continue; + table = new_table; + memcpy(&table[size], &fentry, sizeof(fentry)); + size++; + } + qsort(table, size, sizeof(fentry), + (int (*)(const void *, const void *)) compare); + + for (i = 0; i < size; i++) { + printf("%10d\t", table[i].size, table[i].name); + if (fentry.type == FSP_RDTYPE_DIR && *dircolor) { + printf("", dircolor); + } + printf("%s", table[i].name); + if (fentry.type == FSP_RDTYPE_DIR && *dircolor) { + printf(""); + } + puts(""); + } +} + +static void +fsp_directory(FSP_SESSION *ses, struct uri *uri) +{ + struct string buf; + FSP_DIR *dir; + unsigned char *data = get_uri_string(uri, URI_DATA); + + if (!init_string(&buf)) + fsp_error("Out of memory"); + + add_to_string(&buf, "fsp://"); + if (uri->passwordlen) { + add_to_string(&buf, "u:"); + add_bytes_to_string(&buf, uri->password, uri->passwordlen); + add_char_to_string(&buf, '@'); + } + add_bytes_to_string(&buf, uri->host, uri->hostlen); + if (uri->portlen) { + add_char_to_string(&buf, ':'); + add_bytes_to_string(&buf, uri->port, uri->portlen); + } + add_char_to_string(&buf, '/'); + if (uri->datalen) { + add_bytes_to_string(&buf, uri->data, uri->datalen); + add_char_to_string(&buf, '/'); + } + + printf("%s" + "
", buf.source, buf.source);
+
+	dir = fsp_opendir(ses, data);
+	if (!dir) goto end;
+
+	if (get_opt_bool("protocol.fsp.sort")) {
+		sort_and_display_entries(dir);
+	} else {
+		FSP_RDENTRY fentry, *fresult;
+		unsigned char dircolor[8];
+
+		if (get_opt_bool("document.browse.links.color_dirs")) {
+			color_to_string(get_opt_color("document.colors.dirs"),
+				(unsigned char *) &dircolor);
+		} else {
+			dircolor[0] = 0;
+		}
+		
+		while (!fsp_readdir_native(dir, &fentry, &fresult)) {
+			if (!fresult) break;
+			printf("%10d\t", fentry.size, fentry.name);
+			if (fentry.type == FSP_RDTYPE_DIR && *dircolor) {
+				printf("", dircolor);
+			}
+			printf("%s", fentry.name);
+			if (fentry.type == FSP_RDTYPE_DIR && *dircolor) {
+				printf("");
+			}
+			puts("");
+		}
+		fsp_closedir(dir);
+	}
+end:
+	puts("
"); + fsp_close_session(ses); + exit(0); +} + +#define READ_SIZE 4096 + +static void +do_fsp(struct connection *conn) +{ + struct stat sb; + struct uri *uri = conn->uri; + unsigned char *host = get_uri_string(uri, URI_HOST); + unsigned char *password = get_uri_string(uri, URI_PASSWORD); + unsigned char *data = get_uri_string(uri, URI_DATA); + unsigned short port = (unsigned short)get_uri_port(uri); + FSP_SESSION *ses = fsp_open_session(host, port, password); + + if (!ses) + fsp_error("Session initialization failed."); + if (fsp_stat(ses, data, &sb)) + fsp_error("File not found."); + if (S_ISDIR(sb.st_mode)) + fsp_directory(ses, uri); + else { /* regular file */ + char buf[4096]; + FSP_FILE *file = fsp_fopen(ses, data, "r"); + int r; + + if (!file) + fsp_error("fsp_fopen error."); + + while ((r = fsp_fread(buf, 1, READ_SIZE, file)) > 0) + fwrite(buf, 1, r, stdout); + fsp_fclose(file); + fsp_close_session(ses); + exit(0); + } +} + +static void +fsp_got_data(struct socket *socket, struct read_buffer *rb) +{ + int len = rb->length; + struct connection *conn = socket->conn; + + if (len < 0) { + abort_connection(conn, -errno); + return; + } + if (len == 0) { + if (conn->from) + normalize_cache_entry(conn->cached, conn->from); + close_socket(socket); + abort_connection(conn, S_OK); + return; + } + + conn->socket->state = SOCKET_END_ONCLOSE; + conn->received += len; + if (add_fragment(conn->cached, conn->from, rb->data, len) == 1) + conn->tries = 0; + conn->from += len; + kill_buffer_data(rb, len); + read_from_socket(socket, rb, S_TRANS, fsp_got_data); +} + +#undef READ_SIZE + + +/* Close all non-terminal file descriptors. */ +static void +close_all_non_term_fd(void) +{ + int n; + int max = 1024; +#ifdef RLIMIT_NOFILE + struct rlimit lim; + + if (!getrlimit(RLIMIT_NOFILE, &lim)) + max = lim.rlim_max; +#endif + for (n = 3; n < max; n++) + close(n); +} + +void +fsp_protocol_handler(struct connection *conn) +{ + int fsp_pipe[2] = { -1, -1 }; + int cpid; + struct read_buffer *buf; + + if (c_pipe(fsp_pipe)) { + int s_errno = errno; + + if (fsp_pipe[0] >= 0) close(fsp_pipe[0]); + if (fsp_pipe[1] >= 0) close(fsp_pipe[1]); + abort_connection(conn, -s_errno); + return; + } + + conn->cached = get_cache_entry(conn->uri); + if (!conn->cached) { + abort_connection(conn, S_OUT_OF_MEM); + return; + } + conn->from = 0; + conn->unrestartable = 1; + + cpid = fork(); + if (cpid == -1) { + int s_errno = errno; + + close(fsp_pipe[0]); + close(fsp_pipe[1]); + retry_connection(conn, -s_errno); + return; + } + + if (!cpid) { + close(1); + dup2(fsp_pipe[1], 1); + close(0); + dup2(open("/dev/null", O_RDONLY), 0); + close(2); + dup2(open("/dev/null", O_RDONLY), 2); + close(fsp_pipe[0]); + + close_all_non_term_fd(); + do_fsp(conn); + + } else { + conn->socket->fd = fsp_pipe[0]; + close(fsp_pipe[1]); + buf = alloc_read_buffer(conn->socket); + if (!buf) { + close(fsp_pipe[0]); + abort_connection(conn, S_OUT_OF_MEM); + return; + } + read_from_socket(conn->socket, buf, S_CONN, fsp_got_data); + } +} diff --git a/src/protocol/fsp/fsp.h b/src/protocol/fsp/fsp.h new file mode 100644 index 00000000..b5b9087e --- /dev/null +++ b/src/protocol/fsp/fsp.h @@ -0,0 +1,16 @@ + +#ifndef EL__PROTOCOL_FSP_FSP_H +#define EL__PROTOCOL_FSP_FSP_H + +#include "main/module.h" +#include "protocol/protocol.h" + +extern struct module fsp_protocol_module; + +#ifdef CONFIG_FSP +extern protocol_handler_T fsp_protocol_handler; +#else +#define fsp_protocol_handler NULL +#endif + +#endif diff --git a/src/protocol/protocol.c b/src/protocol/protocol.c index 388b5b67..9ab10da8 100644 --- a/src/protocol/protocol.c +++ b/src/protocol/protocol.c @@ -31,6 +31,7 @@ #include "protocol/data.h" #include "protocol/file/file.h" #include "protocol/finger/finger.h" +#include "protocol/fsp/fsp.h" #include "protocol/ftp/ftp.h" #include "protocol/gopher/gopher.h" #include "protocol/http/http.h" @@ -57,6 +58,7 @@ static const struct protocol_backend protocol_backends[] = { { "data", 0, data_protocol_handler, 0, 0, 1, 0 }, { "file", 0, file_protocol_handler, 1, 0, 0, 0 }, { "finger", 79, finger_protocol_handler, 1, 1, 0, 0 }, + { "fsp", 21, fsp_protocol_handler, 1, 1, 0, 0 }, { "ftp", 21, ftp_protocol_handler, 1, 1, 0, 0 }, { "gopher", 70, gopher_protocol_handler, 1, 1, 0, 0 }, { "http", 80, http_protocol_handler, 1, 1, 0, 0 }, @@ -276,6 +278,9 @@ static struct module *protocol_submodules[] = { #ifdef CONFIG_FINGER &finger_protocol_module, #endif +#ifdef CONFIG_FSP + &fsp_protocol_module, +#endif #ifdef CONFIG_FTP &ftp_protocol_module, #endif diff --git a/src/protocol/protocol.h b/src/protocol/protocol.h index 614c2173..c39e794c 100644 --- a/src/protocol/protocol.h +++ b/src/protocol/protocol.h @@ -14,6 +14,7 @@ enum protocol { PROTOCOL_DATA, PROTOCOL_FILE, PROTOCOL_FINGER, + PROTOCOL_FSP, PROTOCOL_FTP, PROTOCOL_GOPHER, PROTOCOL_HTTP,