mirror of
https://github.com/rkd77/elinks.git
synced 2024-11-04 08:17:17 -05:00
7dfb20d11f
I tested it only on local PDFs. I don't know if temporary files are deleted or not.
844 lines
21 KiB
C
844 lines
21 KiB
C
/* RFC1524 (mailcap file) implementation */
|
|
|
|
/* This file contains various functions for implementing a fair subset of
|
|
* rfc1524.
|
|
*
|
|
* The rfc1524 defines a format for the Multimedia Mail Configuration, which is
|
|
* the standard mailcap file format under Unix which specifies what external
|
|
* programs should be used to view/compose/edit multimedia files based on
|
|
* content type.
|
|
*
|
|
* Copyright (C) 1996-2000 Michael R. Elkins <me@cs.hmc.edu>
|
|
* Copyright (c) 2002-2004 The ELinks project
|
|
*
|
|
* This file was hijacked from the Mutt project <URL:http://www.mutt.org>
|
|
* (version 1.4) on Saturday the 7th December 2002. It has been heavily
|
|
* elinksified. */
|
|
|
|
#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/gettext/libintl.h"
|
|
#include "main/module.h"
|
|
#include "mime/backend/common.h"
|
|
#include "mime/backend/mailcap.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 mailcap_hash_item {
|
|
/* The entries associated with the type */
|
|
LIST_OF(struct mailcap_entry) entries;
|
|
|
|
/* The content type of all @entries. Must be last! */
|
|
unsigned char type[1];
|
|
};
|
|
|
|
struct mailcap_entry {
|
|
LIST_HEAD(struct mailcap_entry);
|
|
|
|
/* To verify if command qualifies. Cannot contain %s formats. */
|
|
unsigned char *testcommand;
|
|
|
|
/* Used to inform the user of the type or handler. */
|
|
unsigned char *description;
|
|
|
|
/* Used to determine between an exact match and a wildtype match. Lower
|
|
* is better. Increased for each sourced file. */
|
|
unsigned int priority;
|
|
|
|
/* Whether the program "blocks" the term. */
|
|
unsigned int needsterminal:1;
|
|
|
|
/* If "| ${PAGER}" should be added. It would of course be better to
|
|
* pipe the output into a buffer and let ELinks display it but this
|
|
* will have to do for now. */
|
|
unsigned int copiousoutput:1;
|
|
|
|
/* The 'raw' unformatted (view)command from the mailcap files. */
|
|
unsigned char command[1];
|
|
};
|
|
|
|
enum mailcap_option {
|
|
MAILCAP_TREE,
|
|
|
|
MAILCAP_ENABLE,
|
|
MAILCAP_PATH,
|
|
MAILCAP_ASK,
|
|
MAILCAP_DESCRIPTION,
|
|
MAILCAP_PRIORITIZE,
|
|
|
|
MAILCAP_OPTIONS
|
|
};
|
|
|
|
static struct option_info mailcap_options[] = {
|
|
INIT_OPT_TREE("mime", N_("Mailcap"),
|
|
"mailcap", 0,
|
|
N_("Options for mailcap support.")),
|
|
|
|
INIT_OPT_BOOL("mime.mailcap", N_("Enable"),
|
|
"enable", 0, 1,
|
|
N_("Enable mailcap support.")),
|
|
|
|
INIT_OPT_STRING("mime.mailcap", N_("Path"),
|
|
"path", 0, DEFAULT_MAILCAP_PATH,
|
|
N_("Mailcap search path. Colon-separated list of files. "
|
|
"Leave as \"\" to use MAILCAP environment variable instead.")),
|
|
|
|
INIT_OPT_BOOL("mime.mailcap", N_("Ask before opening"),
|
|
"ask", 0, 1,
|
|
N_("Ask before using the handlers defined by mailcap.")),
|
|
|
|
INIT_OPT_INT("mime.mailcap", N_("Type query string"),
|
|
"description", 0, 0, 2, 0,
|
|
N_("Type of description to show in \"what to do with "
|
|
"this file\" query dialog:\n"
|
|
"0 is show \"mailcap\"\n"
|
|
"1 is show program to be run\n"
|
|
"2 is show mailcap description field if any;\n"
|
|
" \"mailcap\" otherwise")),
|
|
|
|
INIT_OPT_BOOL("mime.mailcap", N_("Prioritize entries by file"),
|
|
"prioritize", 0, 1,
|
|
N_("Prioritize entries by the order of the files in "
|
|
"the mailcap path. This means that wildcard entries "
|
|
"(like: image/*) will also be checked before deciding "
|
|
"the handler.")),
|
|
|
|
NULL_OPTION_INFO,
|
|
};
|
|
|
|
#define get_opt_mailcap(which) mailcap_options[(which)].option
|
|
#define get_mailcap(which) get_opt_mailcap(which).value
|
|
#define get_mailcap_ask() get_mailcap(MAILCAP_ASK).number
|
|
#define get_mailcap_description() get_mailcap(MAILCAP_DESCRIPTION).number
|
|
#define get_mailcap_enable() get_mailcap(MAILCAP_ENABLE).number
|
|
#define get_mailcap_prioritize() get_mailcap(MAILCAP_PRIORITIZE).number
|
|
#define get_mailcap_path() get_mailcap(MAILCAP_PATH).string
|
|
|
|
/* State variables */
|
|
static struct hash *mailcap_map = NULL;
|
|
|
|
|
|
static inline void
|
|
done_mailcap_entry(struct mailcap_entry *entry)
|
|
{
|
|
if (!entry) return;
|
|
mem_free_if(entry->testcommand);
|
|
mem_free_if(entry->description);
|
|
mem_free(entry);
|
|
}
|
|
|
|
/* Takes care of all initialization of mailcap entries.
|
|
* Clear memory to make freeing it safer later and we get
|
|
* needsterminal and copiousoutput initialized for free. */
|
|
static inline struct mailcap_entry *
|
|
init_mailcap_entry(unsigned char *command, int priority)
|
|
{
|
|
struct mailcap_entry *entry;
|
|
int commandlen = strlen(command);
|
|
|
|
entry = mem_calloc(1, sizeof(*entry) + commandlen);
|
|
if (!entry) return NULL;
|
|
|
|
memcpy(entry->command, command, commandlen);
|
|
|
|
entry->priority = priority;
|
|
|
|
return entry;
|
|
}
|
|
|
|
static inline void
|
|
add_mailcap_entry(struct mailcap_entry *entry, unsigned char *type, int typelen)
|
|
{
|
|
struct mailcap_hash_item *mitem;
|
|
struct hash_item *item;
|
|
|
|
/* Time to get the entry into the mailcap_map */
|
|
/* First check if the type is already checked in */
|
|
item = get_hash_item(mailcap_map, type, typelen);
|
|
if (!item) {
|
|
mitem = mem_alloc(sizeof(*mitem) + typelen);
|
|
if (!mitem) {
|
|
done_mailcap_entry(entry);
|
|
return;
|
|
}
|
|
|
|
safe_strncpy(mitem->type, type, typelen + 1);
|
|
|
|
init_list(mitem->entries);
|
|
|
|
item = add_hash_item(mailcap_map, mitem->type, typelen, mitem);
|
|
if (!item) {
|
|
mem_free(mitem);
|
|
done_mailcap_entry(entry);
|
|
return;
|
|
}
|
|
} else if (item->value) {
|
|
mitem = item->value;
|
|
} else {
|
|
done_mailcap_entry(entry);
|
|
return;
|
|
}
|
|
|
|
add_to_list_end(mitem->entries, entry);
|
|
}
|
|
|
|
/* Parsing of a RFC1524 mailcap file */
|
|
/* The format is:
|
|
*
|
|
* base/type; command; extradefs
|
|
*
|
|
* type can be * for matching all; base with no /type is an implicit
|
|
* wildcard; command contains a %s for the filename to pass, default to pipe on
|
|
* stdin; extradefs are of the form:
|
|
*
|
|
* def1="definition"; def2="define \;";
|
|
*
|
|
* line wraps with a \ at the end of the line, # for comments. */
|
|
/* TODO handle default pipe. Maybe by prepending "cat |" to the command. */
|
|
|
|
/* Returns a NULL terminated RFC 1524 field, while modifying @next to point
|
|
* to the next field. */
|
|
static unsigned char *
|
|
get_mailcap_field(unsigned char **next)
|
|
{
|
|
unsigned char *field;
|
|
unsigned char *fieldend;
|
|
|
|
if (!next || !*next) return NULL;
|
|
|
|
field = *next;
|
|
skip_space(field);
|
|
fieldend = field;
|
|
|
|
/* End field at the next occurence of ';' but not escaped '\;' */
|
|
do {
|
|
/* Handle both if ';' is the first char or if it's escaped */
|
|
if (*fieldend == ';')
|
|
fieldend++;
|
|
|
|
fieldend = strchr(fieldend, ';');
|
|
} while (fieldend && *(fieldend-1) == '\\');
|
|
|
|
if (fieldend) {
|
|
*fieldend = '\0';
|
|
*next = fieldend;
|
|
fieldend--;
|
|
(*next)++;
|
|
skip_space(*next);
|
|
} else {
|
|
*next = NULL;
|
|
fieldend = field + strlen(field) - 1;
|
|
}
|
|
|
|
/* Remove trailing whitespace */
|
|
while (field <= fieldend && isspace(*fieldend))
|
|
*fieldend-- = '\0';
|
|
|
|
return field;
|
|
}
|
|
|
|
/* Parses specific fields (ex: the '=TestCommand' part of 'test=TestCommand').
|
|
* Expects that @field is pointing right after the specifier (ex: 'test'
|
|
* above). Allocates and returns a NULL terminated token, or NULL if parsing
|
|
* fails. */
|
|
|
|
static unsigned char *
|
|
get_mailcap_field_text(unsigned char *field)
|
|
{
|
|
skip_space(field);
|
|
|
|
if (*field == '=') {
|
|
field++;
|
|
skip_space(field);
|
|
|
|
return stracpy(field);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Parse optional extra definitions. Zero return value means syntax error */
|
|
static inline int
|
|
parse_optional_fields(struct mailcap_entry *entry, unsigned char *line)
|
|
{
|
|
while (0xf131d5) {
|
|
unsigned char *field = get_mailcap_field(&line);
|
|
|
|
if (!field) break;
|
|
|
|
if (!c_strncasecmp(field, "needsterminal", 13)) {
|
|
entry->needsterminal = 1;
|
|
|
|
} else if (!c_strncasecmp(field, "copiousoutput", 13)) {
|
|
entry->copiousoutput = 1;
|
|
|
|
} else if (!c_strncasecmp(field, "test", 4)) {
|
|
entry->testcommand = get_mailcap_field_text(field + 4);
|
|
if (!entry->testcommand)
|
|
return 0;
|
|
|
|
/* Find out wether testing requires filename */
|
|
for (field = entry->testcommand; *field; field++)
|
|
if (*field == '%' && *(field+1) == 's') {
|
|
mem_free(entry->testcommand);
|
|
return 0;
|
|
}
|
|
|
|
} else if (!c_strncasecmp(field, "description", 11)) {
|
|
entry->description = get_mailcap_field_text(field + 11);
|
|
if (!entry->description)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Parses whole mailcap files line-by-line adding entries to the map
|
|
* assigning them the given @priority */
|
|
static void
|
|
parse_mailcap_file(unsigned char *filename, unsigned int priority)
|
|
{
|
|
FILE *file = fopen(filename, "rb");
|
|
unsigned 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 mailcap_entry *entry;
|
|
unsigned char *linepos;
|
|
unsigned char *command;
|
|
unsigned char *basetypeend;
|
|
unsigned char *type;
|
|
int typelen;
|
|
|
|
/* Ignore comments */
|
|
if (*line == '#') continue;
|
|
|
|
linepos = line;
|
|
|
|
/* Get type */
|
|
type = get_mailcap_field(&linepos);
|
|
if (!type) continue;
|
|
|
|
/* Next field is the viewcommand */
|
|
command = get_mailcap_field(&linepos);
|
|
if (!command) continue;
|
|
|
|
entry = init_mailcap_entry(command, priority);
|
|
if (!entry) continue;
|
|
|
|
if (!parse_optional_fields(entry, linepos)) {
|
|
done_mailcap_entry(entry);
|
|
usrerror(gettext("Badly formated mailcap entry "
|
|
"for type %s in \"%s\" line %d"),
|
|
type, filename, lineno);
|
|
continue;
|
|
}
|
|
|
|
basetypeend = strchr(type, '/');
|
|
typelen = strlen(type);
|
|
|
|
if (!basetypeend) {
|
|
unsigned char implicitwild[64];
|
|
|
|
if (typelen + 3 > sizeof(implicitwild)) {
|
|
done_mailcap_entry(entry);
|
|
continue;
|
|
}
|
|
|
|
memcpy(implicitwild, type, typelen);
|
|
implicitwild[typelen++] = '/';
|
|
implicitwild[typelen++] = '*';
|
|
implicitwild[typelen++] = '\0';
|
|
add_mailcap_entry(entry, implicitwild, typelen);
|
|
continue;
|
|
}
|
|
|
|
add_mailcap_entry(entry, type, typelen);
|
|
}
|
|
|
|
fclose(file);
|
|
mem_free_if(line); /* Alloced by file_read_line() */
|
|
}
|
|
|
|
|
|
/* When initializing the mailcap map/hash read, parse and build a hash mapping
|
|
* content type to handlers. Map is built from a list of mailcap files.
|
|
*
|
|
* The RFC1524 specifies that a path of mailcap files should be used.
|
|
* o First we check to see if the user supplied any in mime.mailcap.path
|
|
* o Then we check the MAILCAP environment variable.
|
|
* o Finally fall back to reasonable default
|
|
*/
|
|
|
|
static struct hash *
|
|
init_mailcap_map(void)
|
|
{
|
|
unsigned char *path;
|
|
unsigned int priority = 0;
|
|
|
|
mailcap_map = init_hash8();
|
|
if (!mailcap_map) return NULL;
|
|
|
|
/* Try to setup mailcap_path */
|
|
path = get_mailcap_path();
|
|
if (!path || !*path) path = getenv("MAILCAP");
|
|
if (!path) path = DEFAULT_MAILCAP_PATH;
|
|
|
|
while (*path) {
|
|
unsigned char *filename = get_next_path_filename(&path, ':');
|
|
|
|
if (!filename) continue;
|
|
parse_mailcap_file(filename, priority++);
|
|
mem_free(filename);
|
|
}
|
|
|
|
return mailcap_map;
|
|
}
|
|
|
|
static void
|
|
done_mailcap(struct module *module)
|
|
{
|
|
struct hash_item *item;
|
|
int i;
|
|
|
|
if (!mailcap_map) return;
|
|
|
|
foreach_hash_item (item, *mailcap_map, i) {
|
|
struct mailcap_hash_item *mitem = item->value;
|
|
|
|
if (!mitem) continue;
|
|
|
|
while (!list_empty(mitem->entries)) {
|
|
struct mailcap_entry *entry = mitem->entries.next;
|
|
|
|
del_from_list(entry);
|
|
done_mailcap_entry(entry);
|
|
}
|
|
|
|
mem_free(mitem);
|
|
}
|
|
|
|
free_hash(&mailcap_map);
|
|
}
|
|
|
|
#ifndef TEST_MAILCAP
|
|
|
|
static int
|
|
change_hook_mailcap(struct session *ses, struct option *current, struct option *changed)
|
|
{
|
|
if (changed == &get_opt_mailcap(MAILCAP_PATH)
|
|
|| (changed == &get_opt_mailcap(MAILCAP_ENABLE)
|
|
&& !get_mailcap_enable())) {
|
|
done_mailcap(&mailcap_mime_module);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
init_mailcap(struct module *module)
|
|
{
|
|
static const struct change_hook_info mimetypes_change_hooks[] = {
|
|
{ "mime.mailcap", change_hook_mailcap },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
register_change_hooks(mimetypes_change_hooks);
|
|
|
|
if (get_cmd_opt_bool("anonymous"))
|
|
get_mailcap_enable() = 0;
|
|
}
|
|
|
|
#else
|
|
#define init_mailcap NULL
|
|
#endif /* TEST_MAILCAP */
|
|
|
|
/* The command semantics include the following:
|
|
*
|
|
* %s is the filename that contains the mail body data
|
|
* %t is the content type, like text/plain
|
|
* %{parameter} is replaced by the parameter value from the content-type
|
|
* field
|
|
* \% is %
|
|
*
|
|
* Unsupported RFC1524 parameters: these would probably require some doing
|
|
* by Mutt, and can probably just be done by piping the message to metamail:
|
|
*
|
|
* %n is the integer number of sub-parts in the multipart
|
|
* %F is "content-type filename" repeated for each sub-part
|
|
* Only % is supported by subst_file() which is equivalent to %s. */
|
|
|
|
/* The formatting is postponed until the command is needed. This means
|
|
* @type can be NULL. If '%t' is used in command we bail out. */
|
|
static unsigned char *
|
|
format_command(unsigned char *command, unsigned char *type, int copiousoutput)
|
|
{
|
|
struct string cmd;
|
|
|
|
if (!init_string(&cmd)) return NULL;
|
|
|
|
while (*command) {
|
|
unsigned char *start = command;
|
|
|
|
while (*command && *command != '%' && *command != '\\' && *command != '\'')
|
|
command++;
|
|
|
|
if (start < command)
|
|
add_bytes_to_string(&cmd, start, command - start);
|
|
|
|
switch (*command) {
|
|
case '\'': /* Debian's '%s' */
|
|
command++;
|
|
if (!strncmp(command, "%s'", 3)) {
|
|
command += 3;
|
|
add_char_to_string(&cmd, '%');
|
|
} else {
|
|
add_char_to_string(&cmd, '\'');
|
|
}
|
|
break;
|
|
|
|
case '%':
|
|
command++;
|
|
if (!*command) {
|
|
done_string(&cmd);
|
|
return NULL;
|
|
|
|
} else if (*command == 's') {
|
|
add_char_to_string(&cmd, '%');
|
|
|
|
} else if (*command == 't') {
|
|
if (!type) {
|
|
done_string(&cmd);
|
|
return NULL;
|
|
}
|
|
|
|
add_to_string(&cmd, type);
|
|
}
|
|
command++;
|
|
break;
|
|
|
|
case '\\':
|
|
command++;
|
|
if (*command) {
|
|
add_char_to_string(&cmd, *command);
|
|
command++;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return cmd.source;
|
|
}
|
|
|
|
/* Returns first usable mailcap_entry from a list where @entry is the head.
|
|
* Use of @filename is not supported (yet). */
|
|
static struct mailcap_entry *
|
|
check_entries(struct mailcap_hash_item *item)
|
|
{
|
|
struct mailcap_entry *entry;
|
|
|
|
foreach (entry, item->entries) {
|
|
unsigned char *test;
|
|
|
|
/* Accept current if no test is needed */
|
|
if (!entry->testcommand)
|
|
return entry;
|
|
|
|
/* We have to run the test command */
|
|
test = format_command(entry->testcommand, NULL, 0);
|
|
if (test) {
|
|
int exitcode = exe(test);
|
|
|
|
mem_free(test);
|
|
if (!exitcode)
|
|
return entry;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Attempts to find the given type in the mailcap association map. On success,
|
|
* this returns the associated command, else NULL. Type is a string with
|
|
* syntax '<base>/<type>' (ex: 'text/plain')
|
|
*
|
|
* First the given type is looked up. Then the given <base>-type with added
|
|
* wildcard '*' (ex: 'text/<star>'). For each lookup all the associated
|
|
* entries are checked/tested.
|
|
*
|
|
* The lookup supports testing on files. If no file is given (NULL) any tests
|
|
* that need a file will be taken as failed. */
|
|
|
|
static struct mailcap_entry *
|
|
get_mailcap_entry(unsigned char *type)
|
|
{
|
|
struct mailcap_entry *entry;
|
|
struct hash_item *item;
|
|
|
|
item = get_hash_item(mailcap_map, type, strlen(type));
|
|
|
|
/* Check list of entries */
|
|
entry = (item && item->value) ? check_entries(item->value) : NULL;
|
|
|
|
if (!entry || get_mailcap_prioritize()) {
|
|
/* The type lookup has either failed or we need to check
|
|
* the priorities so get the wild card handler */
|
|
struct mailcap_entry *wildcard = NULL;
|
|
unsigned char *wildpos = strchr(type, '/');
|
|
|
|
if (wildpos) {
|
|
int wildlen = wildpos - type + 1; /* include '/' */
|
|
unsigned char *wildtype = memacpy(type, wildlen + 2);
|
|
|
|
if (!wildtype) return NULL;
|
|
|
|
wildtype[wildlen++] = '*';
|
|
wildtype[wildlen] = '\0';
|
|
|
|
item = get_hash_item(mailcap_map, wildtype, wildlen);
|
|
mem_free(wildtype);
|
|
|
|
if (item && item->value)
|
|
wildcard = check_entries(item->value);
|
|
}
|
|
|
|
/* Use @wildcard if its priority is better or @entry is NULL */
|
|
if (wildcard && (!entry || (wildcard->priority < entry->priority)))
|
|
entry = wildcard;
|
|
}
|
|
|
|
return entry;
|
|
}
|
|
|
|
#if defined(HAVE_SETENV) || defined(HAVE_PUTENV)
|
|
/** Set or unset the DISPLAY environment variable before a mailcap
|
|
* check, or restore it afterwards.
|
|
*
|
|
* In a mailcap file, each entry can specify a test command that
|
|
* checks whether the entry is applicable to the user's environment.
|
|
* For example:
|
|
*
|
|
* @verbatim
|
|
* audio/mpegurl; xmms %s; test=test "$DISPLAY" != ""
|
|
* @endverbatim
|
|
*
|
|
* This means the entry should be used only if the DISPLAY environment
|
|
* variable is not empty, i.e. there is an X display. In ELinks,
|
|
* check_entries() runs these test commands, so they inherit the
|
|
* environment variables of the master ELinks process. However, if
|
|
* the user is running ELinks on multiple terminals, then each slave
|
|
* ELinks process has its own environment variables, which may or may
|
|
* not include DISPLAY. Because the actual mailcap command may be run
|
|
* from a slave ELinks process and inherit the environment from it,
|
|
* any test command should also be run in the same environment.
|
|
*
|
|
* This function does not fully implement the ideal described above.
|
|
* Instead, it only sets the DISPLAY environment variable as ":0" if
|
|
* the terminal has any X display at all, or unsets DISPLAY if not.
|
|
* This should be enough for most test commands seen in practice.
|
|
* After the test commands of mailcap entries have been run, this
|
|
* function must be called again to restore DISPLAY.
|
|
*
|
|
* @todo Retrieve all environment variables from the slave process and
|
|
* propagate them to the test commands. Actually, it might be best
|
|
* to fork the test commands from the slave process, so that they
|
|
* would also inherit the controlling tty. However, that would
|
|
* require changing the interlink protocol and might risk deadlocks
|
|
* or memory leaks if a slave terminates without responding.
|
|
*
|
|
* @param xwin
|
|
* Whether the terminal has an associated X display.
|
|
* @param restore
|
|
* If this is 0, the function sets or clears DISPLAY, as described above.
|
|
* If this is 1, the function restores the original value of DISPLAY.
|
|
* There is only room for one saved value; do not nest calls. */
|
|
static void
|
|
set_display(int xwin, int restore)
|
|
{
|
|
static unsigned char *display = NULL;
|
|
|
|
if (!restore) {
|
|
assert(display == NULL);
|
|
if_assert_failed mem_free(display);
|
|
|
|
display = getenv("DISPLAY");
|
|
if (display) display = stracpy(display);
|
|
if (xwin) {
|
|
#ifdef HAVE_SETENV
|
|
setenv("DISPLAY", ":0", 1);
|
|
#else
|
|
putenv("DISPLAY=:0");
|
|
#endif
|
|
} else {
|
|
#ifdef HAVE_UNSETENV
|
|
unsetenv("DISPLAY");
|
|
#else
|
|
putenv("DISPLAY");
|
|
#endif
|
|
}
|
|
} else { /* restore DISPLAY */
|
|
if (display) {
|
|
#ifdef HAVE_SETENV
|
|
setenv("DISPLAY", display, 1);
|
|
#else
|
|
{
|
|
static unsigned char DISPLAY[1024] = "DISPLAY=";
|
|
|
|
/* DISPLAY[1023] == '\0' and we don't let
|
|
* strncpy write that far, so putenv always
|
|
* gets a null-terminated string. */
|
|
strncpy(DISPLAY + 8, display, 1023 - 8);
|
|
putenv(DISPLAY);
|
|
}
|
|
#endif
|
|
mem_free_set(&display, NULL);
|
|
} else {
|
|
#ifdef HAVE_UNSETENV
|
|
unsetenv("DISPLAY");
|
|
#else
|
|
putenv("DISPLAY");
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static struct mime_handler *
|
|
get_mime_handler_mailcap(unsigned char *type, int xwin)
|
|
{
|
|
struct mailcap_entry *entry;
|
|
struct mime_handler *handler;
|
|
unsigned char *program;
|
|
int block;
|
|
|
|
if (!get_mailcap_enable()
|
|
|| (!mailcap_map && !init_mailcap_map()))
|
|
return NULL;
|
|
|
|
#if defined(HAVE_SETENV) || defined(HAVE_PUTENV)
|
|
set_display(xwin, 0);
|
|
#endif
|
|
entry = get_mailcap_entry(type);
|
|
|
|
#if defined(HAVE_SETENV) || defined(HAVE_PUTENV)
|
|
set_display(xwin, 1);
|
|
#endif
|
|
if (!entry) return NULL;
|
|
|
|
program = format_command(entry->command, type, entry->copiousoutput);
|
|
if (!program) return NULL;
|
|
|
|
block = (entry->needsterminal || entry->copiousoutput);
|
|
handler = init_mime_handler(program, entry->description,
|
|
mailcap_mime_module.name,
|
|
get_mailcap_ask(), block);
|
|
mem_free(program);
|
|
|
|
handler->copiousoutput = entry->copiousoutput;
|
|
return handler;
|
|
}
|
|
|
|
|
|
const struct mime_backend mailcap_mime_backend = {
|
|
/* get_content_type: */ NULL,
|
|
/* get_mime_handler: */ get_mime_handler_mailcap,
|
|
};
|
|
|
|
/* Setup the exported module. */
|
|
struct module mailcap_mime_module = struct_module(
|
|
/* name: */ N_("Mailcap"),
|
|
/* options: */ mailcap_options,
|
|
/* hooks: */ NULL,
|
|
/* submodules: */ NULL,
|
|
/* data: */ NULL,
|
|
/* init: */ init_mailcap,
|
|
/* done: */ done_mailcap
|
|
);
|
|
|
|
#ifdef TEST_MAILCAP
|
|
|
|
#include "util/test.h"
|
|
|
|
/* Some ugly shortcuts for getting defined symbols to work. */
|
|
int default_mime_backend,
|
|
install_signal_handler,
|
|
mimetypes_mime_backend,
|
|
program;
|
|
LIST_OF(struct terminal) terminals;
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
unsigned char *format = "description,ask,block,program";
|
|
int has_gotten = 0;
|
|
int i;
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
char *arg = argv[i];
|
|
|
|
if (strncmp(arg, "--", 2))
|
|
break;
|
|
|
|
arg += 2;
|
|
|
|
if (get_test_opt(&arg, "path", &i, argc, argv, "a string")) {
|
|
get_mailcap_path() = arg;
|
|
done_mailcap(NULL);
|
|
|
|
} else if (get_test_opt(&arg, "format", &i, argc, argv, "a string")) {
|
|
format = arg;
|
|
|
|
} else if (get_test_opt(&arg, "get", &i, argc, argv, "a string")) {
|
|
struct mime_handler *handler;
|
|
|
|
if (has_gotten)
|
|
printf("\n");
|
|
has_gotten = 1;
|
|
printf("type: %s\n", arg);
|
|
handler = get_mime_handler_mailcap(arg, 0);
|
|
if (!handler) continue;
|
|
|
|
if (strstr(format, "description"))
|
|
printf("description: %s\n", handler->description);
|
|
|
|
if (strstr(format, "ask"))
|
|
printf("ask: %d\n", handler->ask);
|
|
|
|
if (strstr(format, "block"))
|
|
printf("block: %d\n", handler->block);
|
|
|
|
if (strstr(format, "program"))
|
|
printf("program: %s\n", handler->program);
|
|
|
|
} else {
|
|
die("Unknown argument '%s'", arg - 2);
|
|
}
|
|
}
|
|
|
|
done_mailcap(NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* TEST_MAILCAP */
|