1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00

[dgi] Experimental DGI support.

Dos Gateway Interface was introduced by Arachne browser.
I tested two cases:
file/cdplayer.dgi   |[7]$ecdplayer.exe $s
application/pdf pdf>txt|$epdftotext $1 $2
This commit is contained in:
Witold Filipczyk 2022-05-21 18:17:04 +02:00
parent 24c5295a28
commit 443d42608d
13 changed files with 970 additions and 129 deletions

View File

@ -1,6 +1,7 @@
top_builddir=../../..
include $(top_builddir)/Makefile.config
OBJS-$(CONFIG_DGI) += dgi.o
OBJS-$(CONFIG_MAILCAP) += mailcap.o
OBJS-$(CONFIG_MIMETYPES) += mimetypes.o

View File

@ -21,11 +21,15 @@
/* Backends dynamic area: */
#include "mime/backend/default.h"
#include "mime/backend/dgi.h"
#include "mime/backend/mailcap.h"
#include "mime/backend/mimetypes.h"
static const struct mime_backend *const mime_backends[] = {
&default_mime_backend,
#ifdef CONFIG_DGI
&dgi_mime_backend,
#endif
#ifdef CONFIG_MAILCAP
&mailcap_mime_backend,
#endif

460
src/mime/backend/dgi.c Normal file
View File

@ -0,0 +1,460 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "elinks.h"
#include "config/options.h"
#include "intl/libintl.h"
#include "main/module.h"
#include "mime/backend/common.h"
#include "mime/backend/dgi.h"
#include "mime/mime.h"
#include "osdep/osdep.h" /* For exe() */
#include "session/session.h"
#include "util/file.h"
#include "util/hash.h"
#include "util/lists.h"
#include "util/memory.h"
#include "util/string.h"
struct dgi_hash_item {
/* The entries associated with the type */
LIST_OF(struct dgi_entry) entries;
/* The content type of all @entries. Must be last! */
char type[1];
};
struct dgi_entry {
LIST_HEAD(struct dgi_entry);
char *type;
char *inpext;
char *outext;
/* The 'raw' unformatted (view)command from the dgi files. */
char command[1];
};
/* State variables */
static struct hash *dgi_map = NULL;
enum dgi_option {
DGI_TREE,
DGI_ENABLE,
DGI_MIME_CFG,
DGI_ASK
};
static union option_info dgi_options[] = {
INIT_OPT_TREE("mime", N_("DGI"),
"dgi", OPT_ZERO,
N_("Dos gateway interface specific options.")),
INIT_OPT_BOOL("mime.dgi", N_("Enable"),
"enable", OPT_ZERO, 1,
N_("Enable DGI support.")),
INIT_OPT_STRING("mime.dgi", N_("Config filename"),
"mime_cfg", OPT_ZERO, "mime.cfg",
N_("Filename and location of config file for DGI.")),
INIT_OPT_BOOL("mime.dgi", N_("Ask before opening"),
"ask", OPT_ZERO, 0,
N_("Ask before using the handlers defined by DGI.")),
INIT_OPT_STRING("mime.dgi", N_("Path $a"),
"a", OPT_ZERO, "",
N_("Path to cache.")),
INIT_OPT_STRING("mime.dgi", N_("Path $b"),
"b", OPT_ZERO, "",
N_("Full name of bookmarks.")),
INIT_OPT_STRING("mime.dgi", N_("Path $c"),
"c", OPT_ZERO, "",
N_("Full name of cache index.")),
INIT_OPT_STRING("mime.dgi", N_("Path $d"),
"d", OPT_ZERO, "",
N_("Document name.")),
INIT_OPT_STRING("mime.dgi", N_("Path $e"),
"e", OPT_ZERO, "",
N_("Path to executable files.")),
INIT_OPT_STRING("mime.dgi", N_("Path $f"),
"f", OPT_ZERO, "",
N_("File browser arguments.")),
INIT_OPT_STRING("mime.dgi", N_("Path $g"),
"g", OPT_ZERO, "",
N_("IP address of 1st gateway.")),
INIT_OPT_STRING("mime.dgi", N_("Path $h"),
"h", OPT_ZERO, "",
N_("Full name of History file.")),
INIT_OPT_STRING("mime.dgi", N_("Path $i"),
"i", OPT_ZERO, "",
N_("Your IP address.")),
INIT_OPT_STRING("mime.dgi", N_("Path $j"),
"j", OPT_ZERO, "",
N_("DJPEG arguments.")),
INIT_OPT_STRING("mime.dgi", N_("Path $l"),
"l", OPT_ZERO, "",
N_("Last visited document.")),
INIT_OPT_STRING("mime.dgi", N_("Path $m"),
"m", OPT_ZERO, "",
N_("Path to mail.")),
INIT_OPT_STRING("mime.dgi", N_("Path $n"),
"n", OPT_ZERO, "",
N_("IP address of 1st nameserver.")),
INIT_OPT_STRING("mime.dgi", N_("Path $p"),
"p", OPT_ZERO, "",
N_("Host.")),
INIT_OPT_STRING("mime.dgi", N_("Path $q"),
"q", OPT_ZERO, "",
N_("Filename of query string (file created only "
"when using this macro).")),
INIT_OPT_STRING("mime.dgi", N_("Path $r"),
"r", OPT_ZERO, "",
N_("Horizontal resolution of screen.")),
INIT_OPT_STRING("mime.dgi", N_("Path $s"),
"s", OPT_ZERO, "",
N_("CGI compatible query string.")),
INIT_OPT_STRING("mime.dgi", N_("Path $t"),
"t", OPT_ZERO, "",
N_("Path for temporary files.")),
INIT_OPT_STRING("mime.dgi", N_("Path $u"),
"u", OPT_ZERO, "",
N_("URL of document.")),
INIT_OPT_STRING("mime.dgi", N_("Path $w"),
"w", OPT_ZERO, "",
N_("Download path.")),
INIT_OPT_STRING("mime.dgi", N_("Path $x"),
"x", OPT_ZERO, "",
N_("Netmask.")),
NULL_OPTION_INFO,
};
#define get_opt_dgi(which) dgi_options[(which)].option
#define get_dgi(which) get_opt_dgi(which).value
#define get_dgi_ask() get_dgi(DGI_ASK).number
#define get_dgi_enable() get_dgi(DGI_ENABLE).number
static inline void
done_dgi_entry(struct dgi_entry *entry)
{
if (!entry) return;
mem_free_if(entry->type);
mem_free_if(entry->inpext);
mem_free_if(entry->outext);
mem_free(entry);
}
/* Takes care of all initialization of dgi entries.
* Clear memory to make freeing it safer later and we get. */
static inline struct dgi_entry *
init_dgi_entry(char *type, char *inpext, char *outext, char *command)
{
struct dgi_entry *entry;
int commandlen = strlen(command);
entry = (struct dgi_entry *)mem_calloc(1, sizeof(*entry) + commandlen);
if (!entry) return NULL;
memcpy(entry->command, command, commandlen);
entry->type = stracpy(type);
if (inpext) {
entry->inpext = straconcat(".", inpext, NULL);
}
if (outext) {
entry->outext = straconcat(".", outext, NULL);
}
return entry;
}
static inline void
add_dgi_entry(struct dgi_entry *entry, char *type, int typelen)
{
struct dgi_hash_item *mitem;
struct hash_item *item;
/* Time to get the entry into the dgi_map */
/* First check if the type is already checked in */
item = get_hash_item(dgi_map, type, typelen);
if (!item) {
mitem = (struct dgi_hash_item *)mem_alloc(sizeof(*mitem) + typelen);
if (!mitem) {
done_dgi_entry(entry);
return;
}
safe_strncpy(mitem->type, type, typelen + 1);
init_list(mitem->entries);
item = add_hash_item(dgi_map, mitem->type, typelen, mitem);
if (!item) {
mem_free(mitem);
done_dgi_entry(entry);
return;
}
} else if (item->value) {
mitem = (struct dgi_hash_item *)item->value;
} else {
done_dgi_entry(entry);
return;
}
add_to_list_end(mitem->entries, entry);
}
/* Parses whole mime_cfg file line-by-line adding entries to the map */
static void
parse_dgi_file(char *filename)
{
FILE *file = fopen(filename, "rb");
char *line = NULL;
size_t linelen = MAX_STR_LEN;
int lineno = 1;
if (!file) return;
while ((line = file_read_line(line, &linelen, file, &lineno))) {
struct dgi_entry *entry;
char *linepos;
char *command;
char *basetypeend;
char *type;
char *inpext, *outext;
char *pipe, *greater, *space;
int typelen;
/* Ignore comments */
if (*line == ';' || *line == '[') continue;
linepos = line;
pipe = strchr(linepos, '|');
if (!pipe) continue;
*pipe = '\0';
command = pipe + 1;
greater = strchr(linepos, '>');
if (!greater) {
outext = NULL;
} else {
*greater = '\0';
outext = greater + 1;
}
space = strrchr(linepos, ' ');
if (!space) {
inpext = NULL;
} else {
inpext = space + 1;
}
space = strchr(linepos, ' ');
if (!space) continue;
*space = '\0';
type = linepos;
if (!*type) continue;
entry = init_dgi_entry(type, inpext, outext, command);
if (!entry) continue;
basetypeend = strchr(type, '/');
typelen = strlen(type);
if (!basetypeend) {
char implicitwild[64];
if (typelen + 3 > sizeof(implicitwild)) {
done_dgi_entry(entry);
continue;
}
memcpy(implicitwild, type, typelen);
implicitwild[typelen++] = '/';
implicitwild[typelen++] = '*';
implicitwild[typelen++] = '\0';
add_dgi_entry(entry, implicitwild, typelen);
continue;
}
add_dgi_entry(entry, type, typelen);
}
fclose(file);
mem_free_if(line); /* Alloced by file_read_line() */
}
static struct hash *
init_dgi_map(void)
{
dgi_map = init_hash8();
if (!dgi_map) return NULL;
parse_dgi_file(get_opt_str("mime.dgi.mime_cfg", NULL));
return dgi_map;
}
static void
done_dgi(struct module *module)
{
struct hash_item *item;
int i;
if (!dgi_map) return;
foreach_hash_item (item, *dgi_map, i) {
struct dgi_hash_item *mitem = (struct dgi_hash_item *)item->value;
if (!mitem) continue;
while (!list_empty(mitem->entries)) {
struct dgi_entry *entry = (struct dgi_entry *)mitem->entries.next;
del_from_list(entry);
done_dgi_entry(entry);
}
mem_free(mitem);
}
free_hash(&dgi_map);
}
static int
change_hook_dgi(struct session *ses, struct option *current, struct option *changed)
{
if (changed == &get_opt_dgi(DGI_MIME_CFG)
|| (changed == &get_opt_dgi(DGI_ENABLE)
&& !get_dgi_enable())) {
done_dgi(&dgi_mime_module);
}
return 0;
}
static void
init_dgi(struct module *module)
{
static const struct change_hook_info mimetypes_change_hooks[] = {
{ "mime.dgi.enable", change_hook_dgi },
{ "mime.dgi.mime_cfg", change_hook_dgi },
{ NULL, NULL },
};
register_change_hooks(mimetypes_change_hooks);
if (get_cmd_opt_bool("anonymous"))
get_opt_bool("mime.dgi.enable", NULL) = 0;
}
/* Returns first usable dgi_entry from a list where @entry is the head.
* Use of @filename is not supported (yet). */
static struct dgi_entry *
check_entries(struct dgi_hash_item *item)
{
struct dgi_entry *entry;
foreach (entry, item->entries) {
return entry;
}
return NULL;
}
static struct dgi_entry *
get_dgi_entry(char *type)
{
struct dgi_entry *entry;
struct hash_item *item;
item = get_hash_item(dgi_map, type, strlen(type));
/* Check list of entries */
entry = ((item && item->value) ? check_entries((struct dgi_hash_item *)item->value) : NULL);
if (!entry) {
struct dgi_entry *wildcard = NULL;
char *wildpos = strchr(type, '/');
if (wildpos) {
int wildlen = wildpos - type + 1; /* include '/' */
char *wildtype = memacpy(type, wildlen + 2);
if (!wildtype) return NULL;
wildtype[wildlen++] = '*';
wildtype[wildlen] = '\0';
item = get_hash_item(dgi_map, wildtype, wildlen);
mem_free(wildtype);
if (item && item->value)
wildcard = check_entries((struct dgi_hash_item *)item->value);
}
/* Use @wildcard if its priority is better or @entry is NULL */
if (wildcard && (!entry))
entry = wildcard;
}
return entry;
}
struct mime_handler *
get_mime_handler_dgi(char *type, int xwin)
{
struct dgi_entry *entry;
struct mime_handler *handler;
char *program;
if (!get_dgi_enable()
|| (!dgi_map && !init_dgi_map()))
return NULL;
entry = get_dgi_entry(type);
if (!entry) return NULL;
program = stracpy(entry->command);
if (!program) return NULL;
handler = init_mime_handler(program, NULL,
dgi_mime_module.name,
get_dgi_ask(), 0);
mem_free(program);
handler->inpext = entry->inpext;
handler->outext = entry->outext;
handler->dgi = 1;
return handler;
}
const struct mime_backend dgi_mime_backend = {
/* get_content_type: */ NULL,
/* get_mime_handler: */ get_mime_handler_dgi,
};
/* Setup the exported module. */
struct module dgi_mime_module = struct_module(
/* name: */ N_("DGI mime"),
/* options: */ dgi_options,
/* hooks: */ NULL,
/* submodules: */ NULL,
/* data: */ NULL,
/* init: */ init_dgi,
/* done: */ done_dgi
);

21
src/mime/backend/dgi.h Normal file
View File

@ -0,0 +1,21 @@
#ifndef EL__MIME_BACKEND_DGI_H
#define EL__MIME_BACKEND_DGI_H
#include "main/module.h"
#include "mime/backend/common.h"
#ifdef __cplusplus
extern "C" {
#endif
extern const struct mime_backend dgi_mime_backend;
extern struct module dgi_mime_module;
struct mime_handler *get_mime_handler_dgi(char *type, int xwin);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -1,3 +1,6 @@
if conf_data.get('CONFIG_DGI')
srcs += files('dgi.c')
endif
if conf_data.get('CONFIG_MAILCAP')
srcs += files('mailcap.c')
endif

View File

@ -348,11 +348,15 @@ add_mime_filename_to_string(struct string *string, struct uri *uri)
/* Backends dynamic area: */
#include "mime/backend/default.h"
#include "mime/backend/dgi.h"
#include "mime/backend/mailcap.h"
#include "mime/backend/mimetypes.h"
static struct module *mime_submodules[] = {
&default_mime_module,
#ifdef CONFIG_DGI
&dgi_mime_module,
#endif
#ifdef CONFIG_MAILCAP
&mailcap_mime_module,
#endif

View File

@ -13,9 +13,12 @@ struct uri;
struct mime_handler {
char *description;
const char *backend_name;
char *inpext;
char *outext;
unsigned int ask:1;
unsigned int block:1;
unsigned int copiousoutput:1;
unsigned int dgi:1;
char program[1]; /* XXX: Keep last! */
};

View File

@ -25,6 +25,7 @@
#include "cookies/cookies.h"
#include "intl/libintl.h"
#include "mime/backend/common.h"
#include "mime/backend/dgi.h"
#include "network/connection.h"
#include "network/progress.h"
#include "network/socket.h"
@ -38,92 +39,12 @@
#include "terminal/terminal.h"
#include "util/conv.h"
#include "util/env.h"
#include "util/qs_parse/qs_parse.h"
#include "util/string.h"
static union option_info dgi_options[] = {
INIT_OPT_TREE("protocol.file", N_("DGI"),
"dgi", OPT_ZERO,
N_("Dos gateway interface specific options.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $a"),
"a", OPT_ZERO, "",
N_("Path to cache.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $b"),
"b", OPT_ZERO, "",
N_("Full name of bookmarks.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $c"),
"c", OPT_ZERO, "",
N_("Full name of cache index.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $d"),
"d", OPT_ZERO, "",
N_("Document name.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $e"),
"e", OPT_ZERO, "",
N_("Path to executable files.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $f"),
"f", OPT_ZERO, "",
N_("File browser arguments.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $g"),
"g", OPT_ZERO, "",
N_("IP address of 1st gateway.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $h"),
"h", OPT_ZERO, "",
N_("Full name of History file.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $i"),
"i", OPT_ZERO, "",
N_("Your IP address.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $j"),
"j", OPT_ZERO, "",
N_("DJPEG arguments.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $l"),
"l", OPT_ZERO, "",
N_("Last visited document.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $m"),
"m", OPT_ZERO, "",
N_("Path to mail.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $n"),
"n", OPT_ZERO, "",
N_("IP address of 1st nameserver.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $p"),
"p", OPT_ZERO, "",
N_("Host.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $q"),
"q", OPT_ZERO, "",
N_("Filename of query string (file created only "
"when using this macro).")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $r"),
"r", OPT_ZERO, "",
N_("Horizontal resolution of screen.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $s"),
"s", OPT_ZERO, "",
N_("CGI compatible query string.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $t"),
"t", OPT_ZERO, "",
N_("Path for temporary files.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $u"),
"u", OPT_ZERO, "",
N_("URL of document.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $w"),
"w", OPT_ZERO, "",
N_("Download path.")),
INIT_OPT_STRING("protocol.file.dgi", N_("Path $x"),
"x", OPT_ZERO, "",
N_("Netmask.")),
NULL_OPTION_INFO,
};
struct dgi_entry {
const char *name;
const char *cmdline;
};
struct dgi_entry entries[] = {
{ "cdplayer.dgi", "$ecdplayer.exe $s > $2" },
NULL
};
struct module dgi_protocol_module = struct_module(
/* name: */ N_("Dos Gateway Interface (DGI)"),
/* options: */ dgi_options,
/* name: */ N_("DGI"),
/* options: */ NULL,
/* hooks: */ NULL,
/* submodules: */ NULL,
/* data: */ NULL,
@ -131,25 +52,29 @@ struct module dgi_protocol_module = struct_module(
/* done: */ NULL
);
static struct dgi_entry *
static struct mime_handler *
find_dgi(const char *name)
{
const char *last = strrchr(name, '/');
struct dgi_entry *entry;
struct mime_handler *handler;
if (last) {
name = last + 1;
}
for (entry = entries; entry; entry++) {
if (!entry->name) break;
struct string dtype;
if (!strcmp(name, entry->name)) {
return entry;
}
if (!init_string(&dtype)) {
return NULL;
}
return NULL;
add_to_string(&dtype, "file/");
add_to_string(&dtype, name);
handler = get_mime_handler_dgi(dtype.source, 0);
done_string(&dtype);
return handler;
}
static void
@ -175,16 +100,17 @@ write_request_to_file(struct connection *conn, const char *filename)
enum dgi_state {
NORMAL,
DOLAR,
PERCENT
PERCENT,
LEFT_BRACKET
};
static void
prepare_command(struct dgi_entry *entry, const char *query, struct string *cmd, char **inp, char **out, char **queryfile)
prepare_command(struct mime_handler *handler, const char *query, struct string *cmd, char **inp, char **out, char **queryfile)
{
const char *ch;
enum dgi_state state = NORMAL;
for (ch = entry->cmdline; *ch; ch++) {
for (ch = handler->program; *ch; ch++) {
switch (state) {
case NORMAL:
default:
@ -192,78 +118,89 @@ prepare_command(struct dgi_entry *entry, const char *query, struct string *cmd,
state = DOLAR;
} else if (*ch == '%') {
state = PERCENT;
} else if (*ch == '[') {
state = LEFT_BRACKET;
} else {
add_char_to_string(cmd, *ch);
}
break;
case LEFT_BRACKET:
switch (*ch) {
case ']':
state = NORMAL;
break;
default:
break;
}
break;
case DOLAR:
case PERCENT:
switch(*ch) {
switch (*ch) {
case 'a':
add_to_string(cmd, get_opt_str("protocol.file.dgi.a", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.a", NULL));
state = NORMAL;
break;
case 'b':
add_to_string(cmd, get_opt_str("protocol.file.dgi.b", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.b", NULL));
state = NORMAL;
break;
case 'c':
add_to_string(cmd, get_opt_str("protocol.file.dgi.c", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.c", NULL));
state = NORMAL;
break;
case 'd':
add_to_string(cmd, get_opt_str("protocol.file.dgi.d", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.d", NULL));
state = NORMAL;
break;
case 'e':
add_to_string(cmd, get_opt_str("protocol.file.dgi.e", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.e", NULL));
state = NORMAL;
break;
case 'f':
add_to_string(cmd, get_opt_str("protocol.file.dgi.f", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.f", NULL));
state = NORMAL;
break;
case 'g':
add_to_string(cmd, get_opt_str("protocol.file.dgi.g", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.g", NULL));
state = NORMAL;
break;
case 'h':
add_to_string(cmd, get_opt_str("protocol.file.dgi.h", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.h", NULL));
state = NORMAL;
break;
case 'i':
add_to_string(cmd, get_opt_str("protocol.file.dgi.i", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.i", NULL));
state = NORMAL;
break;
case 'j':
add_to_string(cmd, get_opt_str("protocol.file.dgi.j", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.j", NULL));
state = NORMAL;
break;
case 'l':
add_to_string(cmd, get_opt_str("protocol.file.dgi.l", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.l", NULL));
state = NORMAL;
break;
case 'm':
add_to_string(cmd, get_opt_str("protocol.file.dgi.m", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.m", NULL));
state = NORMAL;
break;
case 'n':
add_to_string(cmd, get_opt_str("protocol.file.dgi.n", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.n", NULL));
state = NORMAL;
break;
case 'p':
add_to_string(cmd, get_opt_str("protocol.file.dgi.p", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.p", NULL));
state = NORMAL;
break;
case 'q':
*queryfile = tempname(NULL, "elinks", ".txt");
*queryfile = tempname(NULL, "elinks", handler->inpext);
if (*queryfile) {
add_to_string(cmd, *queryfile);
}
state = NORMAL;
break;
case 'r':
add_to_string(cmd, get_opt_str("protocol.file.dgi.r", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.r", NULL));
state = NORMAL;
break;
case 's':
@ -273,30 +210,30 @@ prepare_command(struct dgi_entry *entry, const char *query, struct string *cmd,
state = NORMAL;
break;
case 't':
add_to_string(cmd, get_opt_str("protocol.file.dgi.t", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.t", NULL));
state = NORMAL;
break;
case 'u':
add_to_string(cmd, get_opt_str("protocol.file.dgi.u", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.u", NULL));
state = NORMAL;
break;
case 'w':
add_to_string(cmd, get_opt_str("protocol.file.dgi.w", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.w", NULL));
state = NORMAL;
break;
case 'x':
add_to_string(cmd, get_opt_str("protocol.file.dgi.x", NULL));
add_to_string(cmd, get_opt_str("mime.dgi.x", NULL));
state = NORMAL;
break;
case '1':
*inp = tempname(NULL, "elinks", ".txt");
*inp = tempname(NULL, "elinks", handler->inpext);
if (*inp) {
add_to_string(cmd, *inp);
}
state = NORMAL;
break;
case '2':
*out = tempname(NULL, "elinks", ".htm");
*out = tempname(NULL, "elinks", handler->outext);
if (*out) {
add_to_string(cmd, *out);
}
@ -312,11 +249,284 @@ prepare_command(struct dgi_entry *entry, const char *query, struct string *cmd,
}
}
static void
prepare_command2(char *program, const char *filename, char *inpext, char *outext, struct string *cmd, char **inp, char **out)
{
const char *ch;
char *query = NULL;
enum dgi_state state = NORMAL;
for (ch = program; *ch; ch++) {
switch (state) {
case NORMAL:
default:
if (*ch == '$') {
state = DOLAR;
} else if (*ch == '%') {
state = PERCENT;
} else if (*ch == '[') {
state = LEFT_BRACKET;
} else {
add_char_to_string(cmd, *ch);
}
break;
case LEFT_BRACKET:
switch (*ch) {
case ']':
state = NORMAL;
break;
default:
break;
}
break;
case DOLAR:
case PERCENT:
switch (*ch) {
case 'a':
add_to_string(cmd, get_opt_str("mime.dgi.a", NULL));
state = NORMAL;
break;
case 'b':
add_to_string(cmd, get_opt_str("mime.dgi.b", NULL));
state = NORMAL;
break;
case 'c':
add_to_string(cmd, get_opt_str("mime.dgi.c", NULL));
state = NORMAL;
break;
case 'd':
add_to_string(cmd, get_opt_str("mime.dgi.d", NULL));
state = NORMAL;
break;
case 'e':
add_to_string(cmd, get_opt_str("mime.dgi.e", NULL));
state = NORMAL;
break;
case 'f':
add_to_string(cmd, get_opt_str("mime.dgi.f", NULL));
state = NORMAL;
break;
case 'g':
add_to_string(cmd, get_opt_str("mime.dgi.g", NULL));
state = NORMAL;
break;
case 'h':
add_to_string(cmd, get_opt_str("mime.dgi.h", NULL));
state = NORMAL;
break;
case 'i':
add_to_string(cmd, get_opt_str("mime.dgi.i", NULL));
state = NORMAL;
break;
case 'j':
add_to_string(cmd, get_opt_str("mime.dgi.j", NULL));
state = NORMAL;
break;
case 'l':
add_to_string(cmd, get_opt_str("mime.dgi.l", NULL));
state = NORMAL;
break;
case 'm':
add_to_string(cmd, get_opt_str("mime.dgi.m", NULL));
state = NORMAL;
break;
case 'n':
add_to_string(cmd, get_opt_str("mime.dgi.n", NULL));
state = NORMAL;
break;
case 'p':
add_to_string(cmd, get_opt_str("mime.dgi.p", NULL));
state = NORMAL;
break;
case 'q':
state = NORMAL;
break;
case 'r':
add_to_string(cmd, get_opt_str("mime.dgi.r", NULL));
state = NORMAL;
break;
case 's':
if (query) {
add_to_string(cmd, query);
}
state = NORMAL;
break;
case 't':
add_to_string(cmd, get_opt_str("mime.dgi.t", NULL));
state = NORMAL;
break;
case 'u':
add_to_string(cmd, get_opt_str("mime.dgi.u", NULL));
state = NORMAL;
break;
case 'w':
add_to_string(cmd, get_opt_str("mime.dgi.w", NULL));
state = NORMAL;
break;
case 'x':
add_to_string(cmd, get_opt_str("mime.dgi.x", NULL));
state = NORMAL;
break;
case '1':
if (filename) {
add_to_string(cmd, filename);
}
state = NORMAL;
break;
case '2':
*out = tempname(NULL, "elinks", outext);
if (*out) {
add_to_string(cmd, *out);
}
state = NORMAL;
break;
default:
add_char_to_string(cmd, *ch);
state = NORMAL;
break;
}
break;
}
}
}
void
dgi_protocol_handler(struct connection *conn)
{
#define NUMKVPAIRS 16
char *ref, *query;
struct connection_state state = connection_state(S_OK);
int check;
int i;
char *kvpairs[NUMKVPAIRS];
char *command=NULL;
char *filename=NULL;
char *inpext=NULL;
char *outext=NULL;
char *del = NULL;
struct string command_str;
char *tempfilename = NULL;
char *outputfilename = NULL;
/* security checks */
if (!conn->referrer || conn->referrer->protocol != PROTOCOL_DGI) {
goto bad;
}
ref = get_uri_string(conn->referrer, URI_PATH);
if (!ref) {
goto bad;
}
check = strcmp(ref, "/");
mem_free(ref);
if (check) goto bad;
if (!init_string(&command_str)) {
state = connection_state(S_OUT_OF_MEM);
abort_connection(conn, state);
return;
}
query = get_uri_string(conn->uri, URI_QUERY);
if (query) {
i = qs_parse(query, kvpairs, 16);
command = qs_k2v("command", kvpairs, i);
filename = qs_k2v("filename", kvpairs, i);
inpext = qs_k2v("inpext", kvpairs, i);
outext = qs_k2v("outext", kvpairs, i);
del = qs_k2v("delete", kvpairs, i);
}
prepare_command2(command, filename, inpext, outext, &command_str, &tempfilename, &outputfilename);
system(command_str.source);
done_string(&command_str);
if (del) {
unlink(filename);
}
if (tempfilename) {
unlink(tempfilename);
}
if (!outputfilename) {
state = connection_state(S_OK);
abort_connection(conn, state);
mem_free_if(query);
return;
}
struct string page;
struct string name;
if (!init_string(&name)) {
unlink(outputfilename);
mem_free_if(query);
return;
}
add_to_string(&name, outputfilename);
state = read_encoded_file(&name, &page);
unlink(outputfilename);
done_string(&name);
if (is_in_state(state, S_OK)) {
struct cache_entry *cached;
/* Try to add fragment data to the connection cache if either
* file reading or directory listing worked out ok. */
cached = conn->cached = get_cache_entry(conn->uri);
if (!conn->cached) {
state = connection_state(S_OUT_OF_MEM);
} else {
add_fragment(cached, 0, page.source, page.length);
conn->from += page.length;
if (1) {
char *head;
char *otype = NULL;
if (outext) {
otype = get_extension_content_type(outext);
}
if (!otype) {
otype = stracpy("text/html");
}
/* If the system charset somehow
* changes after the directory listing
* has been generated, it should be
* parsed with the original charset. */
head = straconcat("\r\nContent-Type: ", otype, "; charset=",
get_cp_mime_name(get_cp_index("System")),
"\r\n", (char *) NULL);
mem_free_if(otype);
/* Not so gracefully handle failed memory
* allocation. */
if (!head)
state = connection_state(S_OUT_OF_MEM);
/* Setup directory listing for viewing. */
mem_free_set(&cached->head, head);
}
done_string(&page);
}
}
mem_free_if(query);
abort_connection(conn, state);
return;
bad:
abort_connection(conn, connection_state(S_BAD_URL));
}
int
execute_dgi(struct connection *conn)
{
char *script;
struct dgi_entry *entry;
struct mime_handler *handler;
struct string command;
char *tempfilename = NULL;
char *outputfilename = NULL;
@ -335,8 +545,8 @@ execute_dgi(struct connection *conn)
return 0;
}
entry = find_dgi(script);
if (!entry) {
handler = find_dgi(script);
if (!handler) {
mem_free(script);
return 1;
}
@ -348,7 +558,7 @@ execute_dgi(struct connection *conn)
char *query = get_uri_string(conn->uri, URI_QUERY);
prepare_command(entry, query, &command, &tempfilename, &outputfilename, &queryfile);
prepare_command(handler, query, &command, &tempfilename, &outputfilename, &queryfile);
mem_free_if(query);
@ -363,10 +573,6 @@ execute_dgi(struct connection *conn)
fclose(f);
}
}
fprintf(stderr, "%s\n", command.source);
system(command.source);
done_string(&command);
mem_free(script);
@ -381,6 +587,7 @@ execute_dgi(struct connection *conn)
if (!outputfilename) {
mem_free(handler);
state = connection_state(S_OK);
abort_connection(conn, state);
return 0;
@ -391,6 +598,7 @@ execute_dgi(struct connection *conn)
if (!init_string(&name)) {
unlink(outputfilename);
mem_free(handler);
return 0;
}
add_to_string(&name, outputfilename);
@ -412,15 +620,25 @@ execute_dgi(struct connection *conn)
if (1) {
char *head;
char *otype = NULL;
if (handler->outext) {
otype = get_extension_content_type(handler->outext);
}
if (!otype) {
otype = stracpy("text/html");
}
/* If the system charset somehow
* changes after the directory listing
* has been generated, it should be
* parsed with the original charset. */
head = straconcat("\r\nContent-Type: text/html; charset=",
head = straconcat("\r\nContent-Type: ", otype, "; charset=",
get_cp_mime_name(get_cp_index("System")),
"\r\n", (char *) NULL);
mem_free_if(otype);
/* Not so gracefully handle failed memory
* allocation. */
if (!head)
@ -432,6 +650,7 @@ execute_dgi(struct connection *conn)
done_string(&page);
}
}
mem_free(handler);
abort_connection(conn, state);
return 0;
}

View File

@ -1,7 +1,9 @@
#ifndef EL__PROTOCOL_FILE_DGI_H
#define EL__PROTOCOL_FILE_DGI_H
#include "main/module.h"
#include "protocol/protocol.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -10,6 +12,7 @@ struct connection;
struct module;
extern struct module dgi_protocol_module;
extern protocol_handler_T dgi_protocol_handler;
int execute_dgi(struct connection *);
#ifdef __cplusplus

View File

@ -63,6 +63,7 @@ static const struct protocol_backend protocol_backends[] = {
{ "bittorrent", 0, bittorrent_protocol_handler, 0, 0, 1, 0, 1 },
{ "bittorrent-peer",0,bittorrent_peer_protocol_handler, 1, 1, 0, 0, 1 },
{ "data", 0, data_protocol_handler, 0, 0, 1, 0, 1 },
{ "dgi", 0, dgi_protocol_handler, 0, 0, 0, 0, 0 },
{ "file", 0, file_protocol_handler, 1, 0, 0, 0, 0 },
{ "finger", 79, finger_protocol_handler, 1, 1, 0, 0, 1 },
{ "fsp", 21, fsp_protocol_handler, 1, 1, 0, 0, 1 },

View File

@ -17,6 +17,7 @@ enum protocol {
PROTOCOL_BITTORRENT,
PROTOCOL_BITTORRENT_PEER,
PROTOCOL_DATA,
PROTOCOL_DGI,
PROTOCOL_FILE,
PROTOCOL_FINGER,
PROTOCOL_FSP,

View File

@ -156,6 +156,8 @@ abort_download(struct file_download *file_download)
if (file_download->delete_) unlink(file_download->file);
mem_free(file_download->file);
}
mem_free_if(file_download->inpext);
mem_free_if(file_download->outext);
del_from_list(file_download);
mem_free(file_download);
}
@ -355,7 +357,6 @@ do_follow_url_mailcap(struct session *ses, struct uri *uri)
ses_goto(ses, uri, NULL, NULL, CACHE_MODE_NORMAL, TASK_FORWARD, 0);
}
static void
exec_mailcap_command(void *data)
{
@ -406,6 +407,71 @@ exec_later(struct session *ses, char *handler, char *file)
}
}
static void
exec_dgi_command(void *data)
{
struct exec_dgi *exec_dgi = (struct exec_dgi *)data;
if (exec_dgi) {
if (exec_dgi->command) {
struct string string;
if (init_string(&string)) {
static char dgi_dgi[] = "dgi://";
struct uri *ref = get_uri(dgi_dgi, URI_NONE);
struct uri *uri;
struct session *ses = exec_dgi->ses;
add_to_string(&string, "dgi:///dgi?command=");
add_to_string(&string, exec_dgi->command);
add_to_string(&string, "&filename=");
if (exec_dgi->file) {
add_to_string(&string, exec_dgi->file);
}
add_to_string(&string, "&inpext=");
if (exec_dgi->inpext) {
add_to_string(&string, exec_dgi->inpext);
}
add_to_string(&string, "&outext=");
if (exec_dgi->outext) {
add_to_string(&string, exec_dgi->outext);
}
if (exec_dgi->del) {
add_to_string(&string, "&delete=1");
}
uri = get_uri(string.source, URI_BASE_FRAGMENT);
done_string(&string);
set_session_referrer(ses, ref);
if (ref) done_uri(ref);
do_follow_url_mailcap(ses, uri);
if (uri) done_uri(uri);
}
mem_free(exec_dgi->command);
}
mem_free_if(exec_dgi->file);
mem_free_if(exec_dgi->inpext);
mem_free_if(exec_dgi->outext);
mem_free(exec_dgi);
}
}
static void
exec_later_dgi(struct session *ses, char *handler, char *file, char *inpext, char *outext, int del)
{
struct exec_dgi *exec_dgi = (struct exec_dgi *)mem_calloc(1, sizeof(*exec_dgi));
if (exec_dgi) {
exec_dgi->ses = ses;
exec_dgi->command = null_or_stracpy(handler);
exec_dgi->file = null_or_stracpy(file);
exec_dgi->inpext = null_or_stracpy(inpext);
exec_dgi->outext = null_or_stracpy(outext);
exec_dgi->del = del;
register_bottom_half(exec_dgi_command, exec_dgi);
}
}
static void
download_data_store(struct download *download, struct file_download *file_download)
{
@ -462,6 +528,12 @@ download_data_store(struct download *download, struct file_download *file_downlo
file_download->external_handler, file_download->file);
/* Temporary file is deleted by the mailcap_protocol_handler */
file_download->delete_ = 0;
} else if (file_download->dgi) {
exec_later_dgi(file_download->ses,
file_download->external_handler, file_download->file,
file_download->inpext, file_download->outext, 1);
/* Temporary file is deleted by the dgi_protocol_handler */
file_download->delete_ = 0;
} else {
exec_on_terminal(term, file_download->external_handler,
file_download->file,
@ -1260,7 +1332,17 @@ continue_download_do(struct terminal *term, int fd, void *data,
codw_hop->real_file = NULL;
fd = -1;
if (type_query->external_handler) {
if (type_query->dgi && type_query->external_handler) {
file_download->external_handler = type_query->external_handler;
file_download->file = codw_hop->file;
file_download->inpext = null_or_stracpy(type_query->inpext);
file_download->outext = null_or_stracpy(type_query->outext);
file_download->dgi = type_query->dgi;
file_download->delete_ = 1;
/* change owners a few lines above */
codw_hop->file = NULL;
type_query->external_handler = NULL;
} else if (type_query->external_handler) {
file_download->external_handler = subst_file(type_query->external_handler,
codw_hop->file,
type_query->uri->string);
@ -1390,6 +1472,8 @@ done_type_query(struct type_query *type_query)
object_unlock(type_query->cached);
done_uri(type_query->uri);
mem_free_if(type_query->inpext);
mem_free_if(type_query->outext);
mem_free_if(type_query->external_handler);
mem_free_if(type_query->target_frame);
del_from_list(type_query);
@ -1510,6 +1594,17 @@ tp_open(struct type_query *type_query)
char *file = get_uri_string(type_query->uri, URI_PATH);
char *handler = NULL;
if (type_query->dgi) {
if (file) {
decode_uri(file);
}
exec_later_dgi(type_query->ses, type_query->external_handler, file,
type_query->inpext, type_query->outext, 0);
mem_free_if(file);
done_type_query(type_query);
return;
}
if (file) {
decode_uri(file);
handler = subst_file(type_query->external_handler,
@ -1564,6 +1659,9 @@ do_type_query(struct type_query *type_query, char *ct, struct mime_handler *hand
if (handler) {
type_query->block = handler->block;
type_query->copiousoutput = handler->copiousoutput;
type_query->dgi = handler->dgi;
type_query->inpext = null_or_stracpy(handler->inpext);
type_query->outext = null_or_stracpy(handler->outext);
if (!handler->ask) {
type_query->external_handler = stracpy(handler->program);
tp_open(type_query);

View File

@ -100,6 +100,12 @@ struct type_query {
* this frame. This string must be freed with mem_free(). */
char *target_frame;
/** input filename extension */
char *inpext;
/** output filename extension */
char *outext;
/** Command line for an external handler, to be run when the
* download finishes. When ELinks displays the type query,
* it copies this from mime_handler.program of the default
@ -121,6 +127,7 @@ struct type_query {
* from a "file" URI that does not refer to a local CGI, then
* Elinks need not copy the file. */
unsigned int cgi:1;
unsigned int dgi:1;
/** mailcap entry with copiousoutput */
unsigned int copiousoutput:1;
@ -152,6 +159,12 @@ struct file_download {
int notify;
struct download download;
/** input filename extension */
char *inpext;
/** output filename extension */
char *outext;
/** Should the file be deleted when destroying the structure */
unsigned int delete_:1;
@ -161,6 +174,7 @@ struct file_download {
/** Whether to block the terminal when running the external handler. */
unsigned int block:1;
unsigned int dgi:1;
/** Mailcap entry with copiousoutput */
unsigned int copiousoutput:1;
@ -169,6 +183,15 @@ struct file_download {
struct listbox_item *box_item;
};
struct exec_dgi {
struct session *ses;
char *command;
char *file;
char *inpext;
char *outext;
unsigned int del:1;
};
/** Stack of all running downloads */
extern LIST_OF(struct file_download) downloads;