nspluginwrapper/src/npw-config.c

1098 lines
30 KiB
C

/*
* npw-config.c - nspluginwrapper configuration tool
*
* nspluginwrapper (C) 2005-2006 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#include <dlfcn.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <pwd.h>
#include <dirent.h>
static bool g_auto = false;
static bool g_verbose = false;
static const char NPW_CONFIG[] = "nspluginwrapper";
static void error(const char *format, ...)
{
va_list args;
va_start(args, format);
fprintf(stderr, "%s: ", NPW_CONFIG);
vfprintf(stderr, format, args);
fprintf(stderr, "\n");
va_end(args);
exit(1);
}
static int strstart(const char *str, const char *val, const char **ptr)
{
const char *p, *q;
p = str;
q = val;
while (*q != '\0') {
if (*p != *q)
return 0;
p++;
q++;
}
if (ptr)
*ptr = p;
return 1;
}
/* Implement mkdir -p with default permissions (derived from busybox code) */
static int mkdir_p(const char *path)
{
char path_copy[strlen(path) + 1];
char *s = path_copy;
path = strcpy(s, path);
for (;;) {
char c = 0;
while (*s) {
if (*s == '/') {
while (*++s == '/')
;
c = *s;
*s = 0;
break;
}
++s;
}
if (mkdir(path, 0755) < 0) {
struct stat st;
if ((errno != EEXIST && errno != EISDIR) || stat(path, &st) < 0 || !S_ISDIR(st.st_mode))
break;
}
if ((*s = c) == '\0')
return 0;
}
return -1;
}
static const char *get_user_home_dir(void)
{
struct passwd *pwent = getpwuid(geteuid());
if (pwent)
return pwent->pw_dir;
return getenv("HOME");
}
static const char *get_system_mozilla_plugin_dir(void)
{
static const char default_dir[] = LIBDIR "/mozilla/plugins";
static const char *dir = NULL;
if (dir == NULL) {
const char **dirs = NULL;
#if defined(__FreeBSD__)
{
static const char *freebsd_dirs[] = {
"/usr/X11R6/" LIB "/browser_plugins",
"/usr/X11R6/" LIB "/firefox/plugins",
};
dirs = freebsd_dirs;
}
#elif defined(__NetBSD__)
{
static const char *netbsd_dirs[] = {
"/usr/pkg/" LIB "/mozilla/plugins",
"/usr/pkg/" LIB "/firefox/plugins",
};
dirs = netbsd_dirs;
}
#elif defined(__linux__)
if (access("/etc/SuSE-release", F_OK) == 0) {
static const char *suse_dirs[] = {
LIBDIR "/browser-plugins",
LIBDIR "/firefox/plugins",
LIBDIR "/seamonkey/plugins",
"/opt/MozillaFirefox/" LIB "/plugins",
};
dirs = suse_dirs;
}
else if (access("/etc/debian_version", F_OK) == 0) {
static const char *debian_dirs[] = {
"/usr/lib/mozilla/plugins", // XXX how unfortunate
};
dirs = debian_dirs;
}
else if (access("/etc/gentoo-release", F_OK) == 0) {
static const char *gentoo_dirs[] = {
LIBDIR "/nsbrowser/plugins",
};
dirs = gentoo_dirs;
}
#endif
if (dirs) {
while ((dir = *dirs++) != NULL) {
if (access(dir, F_OK) == 0)
break;
}
}
if (dir == NULL)
dir = default_dir;
}
return dir;
}
static const char *get_user_mozilla_plugin_dir(void)
{
const char *home;
static char plugin_path[PATH_MAX];
if ((home = get_user_home_dir()) == NULL)
return NULL;
sprintf(plugin_path, "%s/.mozilla/plugins", home);
return plugin_path;
}
static const char **get_mozilla_plugin_dirs(void)
{
static const char *default_dirs[] = {
"/usr/lib/mozilla/plugins",
"/usr/lib32/mozilla/plugins", // XXX how unfortunate
"/usr/lib64/mozilla/plugins",
"/usr/lib/browser-plugins",
"/usr/lib64/browser-plugins",
"/usr/lib/firefox/plugins",
"/usr/lib64/firefox/plugins",
"/usr/lib/seamonkey/plugins",
"/usr/lib64/seamonkey/plugins",
"/opt/MozillaFirefox/lib/plugins",
"/opt/MozillaFirefox/lib64/plugins",
"/usr/lib/nsbrowser/plugins",
"/usr/lib32/nsbrowser/plugins", // XXX how unfortunate
"/usr/lib64/nsbrowser/plugins",
#if defined(__FreeBSD__)
"/usr/X11R6/lib/browser_plugins",
"/usr/X11R6/lib/firefox/plugins",
"/usr/X11R6/lib/linux-mozilla/plugins",
"/usr/local/lib/npapi/linux-flashplugin",
"/usr/X11R6/Adobe/Acrobat7.0/ENU/Browser/intellinux",
#endif
#if defined(__NetBSD__)
"/usr/pkg/lib/netscape/plugins",
"/usr/pkg/lib/firefox/plugins",
"/usr/pkg/lib/RealPlayer/mozilla",
"/usr/pkg/Acrobat5/Browsers/intellinux",
"/usr/pkg/Acrobat7/Browser/intellinux",
#endif
};
const int n_default_dirs = (sizeof(default_dirs) / sizeof(default_dirs[0]));
const char **dirs = malloc((n_default_dirs + 2) * sizeof(dirs[0]));
int i, j;
for (i = 0, j = 0; i < n_default_dirs; i++) {
const char *dir = default_dirs[i];
if (dir && access(dir, F_OK) == 0)
dirs[j++] = dir;
}
dirs[j++] = get_user_mozilla_plugin_dir();
dirs[j] = NULL;
return dirs;
}
/* ELF decoder derived from QEMU code */
#undef bswap_16
#define bswap_16(x) \
((((x) >> 8) & 0xff) | (((x) & 0xff) << 8))
#undef bswap_32
#define bswap_32(x) \
((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \
(((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24))
/* 32-bit ELF base types. */
typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Word;
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
/* The ELF file header. This appears at the start of every ELF file. */
#define EI_NIDENT (16)
typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */
Elf32_Half e_type; /* Object file type */
Elf32_Half e_machine; /* Architecture */
Elf32_Word e_version; /* Object file version */
Elf32_Addr e_entry; /* Entry point virtual address */
Elf32_Off e_phoff; /* Program header table file offset */
Elf32_Off e_shoff; /* Section header table file offset */
Elf32_Word e_flags; /* Processor-specific flags */
Elf32_Half e_ehsize; /* ELF header size in bytes */
Elf32_Half e_phentsize; /* Program header table entry size */
Elf32_Half e_phnum; /* Program header table entry count */
Elf32_Half e_shentsize; /* Section header table entry size */
Elf32_Half e_shnum; /* Section header table entry count */
Elf32_Half e_shstrndx; /* Section header string table index */
} Elf32_Ehdr;
#define EI_MAG0 0 /* File identification byte 0 index */
#define ELFMAG0 0x7f /* Magic number byte 0 */
#define EI_MAG1 1 /* File identification byte 1 index */
#define ELFMAG1 'E' /* Magic number byte 1 */
#define EI_MAG2 2 /* File identification byte 2 index */
#define ELFMAG2 'L' /* Magic number byte 2 */
#define EI_MAG3 3 /* File identification byte 3 index */
#define ELFMAG3 'F' /* Magic number byte 3 */
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASS32 1 /* 32-bit objects */
#define ELFCLASS64 2 /* 64-bit objects */
#define EI_DATA 5 /* Data encoding byte index */
#define ELFDATA2LSB 1 /* 2's complement, little endian */
#define ELFDATA2MSB 2 /* 2's complement, big endian */
#define EI_OSABI 7 /* OS ABI identification */
#define ELFOSABI_SYSV 0 /* UNIX System V ABI */
#define ELFOSABI_NETBSD 2 /* NetBSD. */
#define ELFOSABI_LINUX 3 /* Linux. */
#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
#define ET_DYN 3 /* Shared object file */
#define EM_386 3 /* Intel 80386 */
#define EM_SPARC 2 /* SUN SPARC */
#define EM_PPC 20 /* PowerPC */
#define EM_PPC64 21 /* PowerPC 64-bit */
#define EM_SPARCV9 43 /* SPARC v9 64-bit */
#define EM_IA_64 50 /* Intel Merced */
#define EM_X86_64 62 /* AMD x86-64 architecture */
#define EV_CURRENT 1 /* Current version */
/* Section header. */
typedef struct
{
Elf32_Word sh_name; /* Section name (string tbl index) */
Elf32_Word sh_type; /* Section type */
Elf32_Word sh_flags; /* Section flags */
Elf32_Addr sh_addr; /* Section virtual addr at execution */
Elf32_Off sh_offset; /* Section file offset */
Elf32_Word sh_size; /* Section size in bytes */
Elf32_Word sh_link; /* Link to another section */
Elf32_Word sh_info; /* Additional section information */
Elf32_Word sh_addralign; /* Section alignment */
Elf32_Word sh_entsize; /* Entry size if section holds table */
} Elf32_Shdr;
#define SHT_NOBITS 8 /* Program space with no data (bss) */
#define SHT_DYNSYM 11 /* Dynamic linker symbol table */
/* Symbol table entry. */
typedef struct
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Half st_shndx; /* Section index */
} Elf32_Sym;
#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4)
#define ELF32_ST_TYPE(val) ((val) & 0xf)
#define STB_GLOBAL 1 /* Global symbol */
#define STT_OBJECT 1 /* Symbol is a data object */
#define STT_FUNC 2 /* Symbol is a code object */
/* We handle 32-bit ELF plugins only */
#undef ELF_CLASS
#define ELF_CLASS ELFCLASS32
#define ElfW(x) Elf32_ ## x
#define ELFW(x) ELF32_ ## x
void *load_data(int fd, long offset, unsigned int size)
{
char *data = (char *)malloc(size);
if (!data)
return NULL;
lseek(fd, offset, SEEK_SET);
if (read(fd, data, size) != size) {
free(data);
return NULL;
}
return data;
}
static bool is_little_endian(void)
{
union { uint32_t i; uint8_t b[4]; } x;
x.i = 0x01020304;
return x.b[0] == 0x04;
}
static void elf_swap_ehdr(ElfW(Ehdr) *hdr)
{
hdr->e_type = bswap_16(hdr->e_type);
hdr->e_machine = bswap_16(hdr->e_machine);
hdr->e_version = bswap_32(hdr->e_version);
hdr->e_entry = bswap_32(hdr->e_entry);
hdr->e_phoff = bswap_32(hdr->e_phoff);
hdr->e_shoff = bswap_32(hdr->e_shoff);
hdr->e_flags = bswap_32(hdr->e_flags);
hdr->e_ehsize = bswap_16(hdr->e_ehsize);
hdr->e_phentsize = bswap_16(hdr->e_phentsize);
hdr->e_phnum = bswap_16(hdr->e_phnum);
hdr->e_shentsize = bswap_16(hdr->e_shentsize);
hdr->e_shnum = bswap_16(hdr->e_shnum);
hdr->e_shstrndx = bswap_16(hdr->e_shstrndx);
}
static void elf_swap_shdr(ElfW(Shdr) *shdr)
{
shdr->sh_name = bswap_32(shdr->sh_name);
shdr->sh_type = bswap_32(shdr->sh_type);
shdr->sh_flags = bswap_32(shdr->sh_flags);
shdr->sh_addr = bswap_32(shdr->sh_addr);
shdr->sh_offset = bswap_32(shdr->sh_offset);
shdr->sh_size = bswap_32(shdr->sh_size);
shdr->sh_link = bswap_32(shdr->sh_link);
shdr->sh_info = bswap_32(shdr->sh_info);
shdr->sh_addralign = bswap_32(shdr->sh_addralign);
shdr->sh_entsize = bswap_32(shdr->sh_entsize);
}
static void elf_swap_sym(ElfW(Sym) *sym)
{
sym->st_name = bswap_32(sym->st_name);
sym->st_value = bswap_32(sym->st_value);
sym->st_size = bswap_32(sym->st_size);
sym->st_shndx = bswap_32(sym->st_shndx);
}
static bool is_plugin_fd(int fd, NPW_PluginInfo *out_plugin_info)
{
int i;
bool ret = false;
ElfW(Ehdr) ehdr;
if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr))
return false;
if (ehdr.e_ident[EI_MAG0] != ELFMAG0
|| ehdr.e_ident[EI_MAG1] != ELFMAG1
|| ehdr.e_ident[EI_MAG2] != ELFMAG2
|| ehdr.e_ident[EI_MAG3] != ELFMAG3)
return false;
bool do_swap = (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) && !is_little_endian();
if (do_swap)
elf_swap_ehdr(&ehdr);
if (ehdr.e_ident[EI_CLASS] != ELF_CLASS)
return false;
if (ehdr.e_type != ET_DYN)
return false;
if (ehdr.e_version != EV_CURRENT)
return false;
if (out_plugin_info) {
const char *target_arch = "";
switch (ehdr.e_machine) {
case EM_386: target_arch = "i386"; break;
case EM_SPARC: target_arch = "sparc"; break;
case EM_PPC: target_arch = "ppc"; break;
}
strcpy(out_plugin_info->target_arch, target_arch);
const char *target_os = "";
switch (ehdr.e_ident[EI_OSABI]) {
case ELFOSABI_LINUX: target_os = "linux"; break;
case ELFOSABI_SOLARIS: target_os = "solaris"; break;
case ELFOSABI_FREEBSD: target_os = "freebsd"; break;
}
strcpy(out_plugin_info->target_os, target_os);
}
ElfW(Shdr) *shdr = (ElfW(Shdr) *)load_data(fd, ehdr.e_shoff, ehdr.e_shnum * sizeof(*shdr));
if (do_swap) {
for (i = 0; i < ehdr.e_shnum; i++)
elf_swap_shdr(&shdr[i]);
}
char **sdata = (char **)calloc(ehdr.e_shnum, sizeof(*sdata));
for (i = 0; i < ehdr.e_shnum; i++) {
ElfW(Shdr) *sec = &shdr[i];
if (sec->sh_type != SHT_NOBITS)
sdata[i] = (char *)load_data(fd, sec->sh_offset, sec->sh_size);
}
ElfW(Shdr) *symtab_sec = NULL;
for (i = 0; i < ehdr.e_shnum; i++) {
ElfW(Shdr) *sec = &shdr[i];
if (sec->sh_type == SHT_DYNSYM
&& strcmp(sdata[ehdr.e_shstrndx] + sec->sh_name, ".dynsym") == 0) {
symtab_sec = sec;
break;
}
}
if (symtab_sec == NULL)
goto done;
ElfW(Sym) *symtab = (ElfW(Sym) *)sdata[symtab_sec - shdr];
char *strtab = sdata[symtab_sec->sh_link];
int nb_syms = symtab_sec->sh_size / sizeof(*symtab);
if (do_swap) {
for (i = 0; i < nb_syms; i++)
elf_swap_sym(&symtab[i]);
}
int nb_np_syms;
int is_wrapper_plugin = 0;
for (i = 0, nb_np_syms = 0; i < nb_syms; i++) {
ElfW(Sym) *sym = &symtab[i];
const char *name = strtab + sym->st_name;
if (ELFW(ST_BIND)(sym->st_info) != STB_GLOBAL)
continue;
if (ELFW(ST_TYPE)(sym->st_info) == STT_OBJECT && strcmp(name, "NPW_Plugin") == 0)
is_wrapper_plugin = 1;
if (ELFW(ST_TYPE)(sym->st_info) != STT_FUNC)
continue;
if (!strcmp(name, "NP_GetMIMEDescription") ||
!strcmp(name, "NP_Initialize") ||
!strcmp(name, "NP_Shutdown"))
nb_np_syms++;
}
ret = (nb_np_syms == 3) && !is_wrapper_plugin;
done:
for (i = 0; i < ehdr.e_shnum; i++)
free(sdata[i]);
free(sdata);
free(shdr);
return ret;
}
static bool is_plugin_viewer_available(const char *filename, NPW_PluginInfo *out_plugin_info)
{
static const char *target_arch_table[] = {
NULL,
"i386",
NULL
};
const int target_arch_table_size = sizeof(target_arch_table) / sizeof(target_arch_table[0]);
if (out_plugin_info && out_plugin_info->target_arch[0] != '\0')
target_arch_table[0] = out_plugin_info->target_arch;
else
target_arch_table[0] = NULL;
static const char *target_os_table[] = {
NULL,
"linux",
NULL
};
const int target_os_table_size = sizeof(target_os_table) / sizeof(target_os_table[0]);
if (out_plugin_info && out_plugin_info->target_os[0] != '\0')
target_os_table[0] = out_plugin_info->target_os;
else
target_os_table[0] = NULL;
for (int i = 0; i < target_arch_table_size; i++) {
const char *target_arch = target_arch_table[i];
if (target_arch == NULL)
continue;
char viewer_arch_path[PATH_MAX];
sprintf(viewer_arch_path, "%s/%s", NPW_LIBDIR, target_arch);
if (access(viewer_arch_path, F_OK) != 0) {
target_arch_table[i] = NULL; // this target ARCH is not available, skip it for good
continue;
}
for (int j = 0; j < target_os_table_size; j++) {
const char *target_os = target_os_table[j];
if (target_os == NULL)
continue;
if (strcmp(target_arch, HOST_ARCH) == 0 && strcmp(target_os, HOST_OS) == 0)
continue; // skip viewers that match host OS/ARCH pairs
char viewer_path[PATH_MAX];
sprintf(viewer_path, "%s/%s/%s", viewer_arch_path, target_os, NPW_VIEWER);
if (access(viewer_path, F_OK) != 0)
continue;
int pid = fork();
if (pid < 0)
continue;
else if (pid == 0) {
execl(viewer_path, NPW_VIEWER, "--test", "--plugin", filename, NULL);
exit(1);
}
else {
int status;
while (waitpid(pid, &status, 0) != pid)
;
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
if (out_plugin_info) {
strcpy(out_plugin_info->target_arch, target_arch);
strcpy(out_plugin_info->target_os, target_os);
}
return true;
}
}
}
}
return false;
}
static bool is_plugin(const char *filename, NPW_PluginInfo *out_plugin_info)
{
int fd = open(filename, O_RDONLY);
if (fd < 0)
return false;
bool ret = is_plugin_fd(fd, out_plugin_info);
close(fd);
return ret;
}
static bool is_compatible_plugin(const char *filename, NPW_PluginInfo *out_plugin_info)
{
return is_plugin(filename, out_plugin_info) && is_plugin_viewer_available(filename, out_plugin_info);
}
static bool is_wrapper_plugin_handle(void *handle, NPW_PluginInfo *out_plugin_info)
{
if (dlsym(handle, "NP_Initialize") == NULL)
return false;
if (dlsym(handle, "NP_Shutdown") == NULL)
return false;
if (dlsym(handle, "NP_GetMIMEDescription") == NULL)
return false;
NPW_PluginInfo *pi;
if ((pi = (NPW_PluginInfo *)dlsym(handle, "NPW_Plugin")) == NULL)
return false;
if (out_plugin_info) {
strcpy(out_plugin_info->ident, pi->ident);
strcpy(out_plugin_info->path, pi->path);
out_plugin_info->mtime = pi->mtime;
out_plugin_info->target_arch[0] = '\0';
out_plugin_info->target_os[0] = '\0';
if (strncmp(pi->ident, "NPW:0.9.90", 10) != 0) { // additional members in 0.9.91+
strcpy(out_plugin_info->target_arch, pi->target_arch);
strcpy(out_plugin_info->target_os, pi->target_os);
}
}
return true;
}
static bool is_wrapper_plugin(const char *plugin_path, NPW_PluginInfo *out_plugin_info)
{
void *handle = dlopen(plugin_path, RTLD_LAZY);
if (handle == NULL)
return false;
bool ret = is_wrapper_plugin_handle(handle, out_plugin_info);
dlclose(handle);
return ret;
}
static bool is_wrapper_plugin_0(const char *plugin_path)
{
NPW_PluginInfo plugin_info;
return is_wrapper_plugin(plugin_path, &plugin_info)
&& strcmp(plugin_info.path, NPW_DEFAULT_PLUGIN_PATH) != 0 // exclude OS/ARCH npwrapper.so
&& strcmp(plugin_info.path, NPW_OLD_DEFAULT_PLUGIN_PATH) != 0; // exclude ARCH npwrapper.so
}
static bool is_system_wide_wrapper_plugin(const char *plugin_path)
{
char *plugin_base = strrchr(plugin_path, '/');
if (plugin_base == NULL)
return false;
plugin_base += 1;
char s_plugin_path[PATH_MAX];
int n = snprintf(s_plugin_path, sizeof(s_plugin_path), "%s/%s.%s", get_system_mozilla_plugin_dir(), NPW_WRAPPER_BASE, plugin_base);
if (n < 0 || n >= sizeof(s_plugin_path))
return false;
struct stat st;
if (stat(plugin_path, &st) < 0)
return false;
NPW_PluginInfo plugin_info;
return (is_wrapper_plugin(s_plugin_path, &plugin_info)
&& strcmp(plugin_info.path, plugin_path) == 0
&& strcmp(plugin_info.ident, NPW_PLUGIN_IDENT) == 0
&& plugin_info.mtime == st.st_mtime);
}
typedef bool (*is_plugin_cb)(const char *plugin_path, NPW_PluginInfo *plugin_info);
typedef int (*process_plugin_cb)(const char *plugin_path, NPW_PluginInfo *plugin_info);
static int process_plugin_dir(const char *plugin_dir, is_plugin_cb test, process_plugin_cb process)
{
if (g_verbose)
printf("Looking for plugins in %s\n", plugin_dir);
DIR *dir = opendir(plugin_dir);
if (dir == NULL)
return -1;
int plugin_path_length = 256;
char *plugin_path = (char *)malloc(plugin_path_length);
int plugin_dir_length = strlen(plugin_dir);
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
int len = plugin_dir_length + 1 + strlen(ent->d_name) + 1;
if (len > plugin_path_length) {
plugin_path_length = len;
plugin_path = (char *)realloc(plugin_path, plugin_path_length);
}
sprintf(plugin_path, "%s/%s", plugin_dir, ent->d_name);
NPW_PluginInfo plugin_info;
if (test(plugin_path, &plugin_info))
process(plugin_path, &plugin_info);
}
free(plugin_path);
closedir(dir);
return 0;
}
static int do_install_plugin(const char *plugin_path, const char *plugin_dir, NPW_PluginInfo *plugin_info)
{
if (plugin_dir == NULL)
return 1;
char *plugin_base = strrchr(plugin_path, '/');
if (plugin_base == NULL)
return 2;
plugin_base += 1;
char d_plugin_path[PATH_MAX];
int n = snprintf(d_plugin_path, sizeof(d_plugin_path), "%s/%s.%s", plugin_dir, NPW_WRAPPER_BASE, plugin_base);
if (n < 0 || n >= sizeof(d_plugin_path))
return 3;
int mode = 0700;
if (geteuid() == 0 && strcmp(plugin_dir, "/root") != 0)
mode = 0755;
NPW_PluginInfo w_plugin_info;
if (!is_wrapper_plugin(NPW_DEFAULT_PLUGIN_PATH, &w_plugin_info))
return 5;
const char *w_plugin_path, *plugin_ident;
plugin_ident = w_plugin_info.ident;
w_plugin_path = w_plugin_info.path;
if (strcmp(w_plugin_path, NPW_DEFAULT_PLUGIN_PATH) != 0)
return 6;
int w_plugin_path_length = strlen(w_plugin_path);
int plugin_ident_length = strlen(plugin_ident);
int w_fd = open(w_plugin_path, O_RDONLY);
if (w_fd < 0)
return 7;
ssize_t w_size = lseek(w_fd, 0, SEEK_END);
if (w_size < 0)
return 8;
lseek(w_fd, 0, SEEK_SET);
char *plugin_data = malloc(w_size);
if (plugin_data == NULL)
return 9;
if (read(w_fd, plugin_data, w_size) != w_size)
return 10;
close(w_fd);
int i, ofs = -1;
for (i = NPW_PLUGIN_IDENT_SIZE; i < w_size - PATH_MAX; i++) {
if (memcmp(plugin_data + i, w_plugin_path, w_plugin_path_length) == 0 &&
memcmp(plugin_data + i - NPW_PLUGIN_IDENT_SIZE, NPW_PLUGIN_IDENT, plugin_ident_length) == 0) {
ofs = i;
break;
}
}
if (ofs < 0)
return 11;
strcpy(plugin_data + ofs, plugin_path);
struct stat st;
if (stat(plugin_path, &st) < 0)
return 12;
if (plugin_info == NULL)
return 14;
if (plugin_info->target_arch[0] == '\0' || plugin_info->target_os[0] == '\0') {
if (!is_plugin_viewer_available(plugin_path, plugin_info))
return 15;
}
NPW_PluginInfo *pi = (NPW_PluginInfo *)(plugin_data + ofs - NPW_PLUGIN_IDENT_SIZE);
pi->mtime = st.st_mtime;
strcpy(pi->target_arch, plugin_info->target_arch);
strcpy(pi->target_os, plugin_info->target_os);
int d_fd = open(d_plugin_path, O_CREAT | O_WRONLY, mode);
if (d_fd < 0)
return 4;
if (write(d_fd, plugin_data, w_size) != w_size)
return 13;
close(d_fd);
if (g_verbose)
printf(" into %s\n", d_plugin_path);
free(plugin_data);
return 0;
}
static int install_plugin(const char *plugin_path, NPW_PluginInfo *plugin_info)
{
int ret;
if (g_verbose)
printf("Install plugin %s\n", plugin_path);
ret = do_install_plugin(plugin_path, get_system_mozilla_plugin_dir(), plugin_info);
if (ret == 0)
return 0;
// don't install plugin in user home dir if already available system-wide
if (is_system_wide_wrapper_plugin(plugin_path)) {
if (g_verbose)
printf(" ... already installed system-wide, skipping\n");
return 0;
}
const char *user_plugin_dir = get_user_mozilla_plugin_dir();
if (access(user_plugin_dir, R_OK | W_OK) < 0 && mkdir_p(user_plugin_dir) < 0)
return 1;
ret = do_install_plugin(plugin_path, user_plugin_dir, plugin_info);
if (ret == 0)
return 0;
return ret;
}
static int auto_install_plugins(void)
{
const char **plugin_dirs = get_mozilla_plugin_dirs();
if (plugin_dirs) {
int i;
for (i = 0; plugin_dirs[i] != NULL; i++) {
const char *plugin_dir = plugin_dirs[i];
if (g_verbose)
printf("Auto-install plugins from %s\n", plugin_dir);
process_plugin_dir(plugin_dir, is_compatible_plugin, (process_plugin_cb)install_plugin);
}
}
free(plugin_dirs);
return 0;
}
static int remove_plugin(const char *plugin_path, ...)
{
if (g_verbose)
printf("Remove plugin %s\n", plugin_path);
if (unlink(plugin_path) < 0)
return 1;
return 0;
}
static int auto_remove_plugins(void)
{
const char **plugin_dirs = get_mozilla_plugin_dirs();
if (plugin_dirs) {
int i;
for (i = 0; plugin_dirs[i] != NULL; i++) {
const char *plugin_dir = plugin_dirs[i];
if (g_verbose)
printf("Auto-remove plugins from %s\n", plugin_dir);
process_plugin_dir(plugin_dir, (is_plugin_cb)is_wrapper_plugin_0, (process_plugin_cb)remove_plugin);
}
}
free(plugin_dirs);
return 0;
}
static int update_plugin(const char *plugin_path, ...)
{
if (g_verbose)
printf("Update plugin %s\n", plugin_path);
int ret = 0;
NPW_PluginInfo plugin_info;
is_wrapper_plugin(plugin_path, &plugin_info);
struct stat st;
if (access(plugin_info.path, F_OK) < 0) {
if (g_verbose)
printf(" NS4 plugin %s is no longer available, removing wrapper\n", plugin_info.path);
ret = remove_plugin(plugin_path);
}
else if (is_system_wide_wrapper_plugin(plugin_info.path)
&& !strstart(plugin_path, get_system_mozilla_plugin_dir(), NULL)) {
if (g_verbose)
printf(" NS4 plugin %s is already installed system-wide, removing wrapper\n", plugin_info.path);
ret = remove_plugin(plugin_path);
}
else if (stat(plugin_info.path, &st) == 0 && st.st_mtime > plugin_info.mtime) {
if (g_verbose)
printf(" NS4 plugin %s was modified, reinstalling plugin\n", plugin_info.path);
ret = install_plugin(plugin_info.path, &plugin_info);
}
else if (strcmp(plugin_info.ident, NPW_PLUGIN_IDENT) != 0) {
if (g_verbose)
printf(" nspluginwrapper ident mismatch, reinstalling plugin\n");
ret = install_plugin(plugin_info.path, &plugin_info);
}
return ret;
}
static int auto_update_plugins(void)
{
const char **plugin_dirs = get_mozilla_plugin_dirs();
if (plugin_dirs) {
int i;
for (i = 0; plugin_dirs[i] != NULL; i++) {
const char *plugin_dir = plugin_dirs[i];
if (g_verbose)
printf("Auto-update plugins from %s\n", plugin_dir);
process_plugin_dir(plugin_dir, (is_plugin_cb)is_wrapper_plugin_0, (process_plugin_cb)update_plugin);
}
}
free(plugin_dirs);
return 0;
}
static int list_plugin(const char *plugin_path, ...)
{
NPW_PluginInfo plugin_info;
is_wrapper_plugin(plugin_path, &plugin_info);
printf("%s\n", plugin_path);
printf(" Original plugin: %s\n", plugin_info.path);
char *str = strtok(plugin_info.ident, ":");
if (str && strcmp(str, "NPW") == 0) {
str = strtok(NULL, ":");
if (str) {
printf(" Wrapper version string: %s", str);
str = strtok(NULL, ":");
if (str)
printf(" (%s)", str);
printf("\n");
}
}
return 0;
}
static void print_usage(void)
{
printf("%s, configuration tool. Version %s\n", NPW_CONFIG, NPW_VERSION);
printf("\n");
printf(" usage: %s [flags] [command [plugin(s)]]\n", NPW_CONFIG);
printf("\n");
printf(" -h --help print this message\n");
printf(" -v --verbose flag: set verbose mode\n");
printf(" -a --auto flag: set automatic mode for plugins discovery\n");
printf(" -l --list list plugins currently installed\n");
printf(" -u --update update plugin(s) currently installed\n");
printf(" -i --install [FILE(S)] install plugin(s)\n");
printf(" -r --remove [FILE(S)] remove plugin(s)\n");
printf("\n");
}
static int process_help(int argc, char *argv[])
{
print_usage();
return 0;
}
static int process_verbose(int argc, char *argv[])
{
g_verbose = true;
return 0;
}
static int process_auto(int argc, char *argv[])
{
g_auto = true;
return 0;
}
static int process_list(int argvc, char *argv[])
{
const char **plugin_dirs = get_mozilla_plugin_dirs();
if (plugin_dirs) {
int i;
for (i = 0; plugin_dirs[i] != NULL; i++) {
const char *plugin_dir = plugin_dirs[i];
if (g_verbose)
printf("List plugins in %s\n", plugin_dir);
process_plugin_dir(plugin_dir, (is_plugin_cb)is_wrapper_plugin_0, (process_plugin_cb)list_plugin);
}
}
free(plugin_dirs);
return 0;
}
static int process_update(int argc, char *argv[])
{
int i;
if (g_auto)
return auto_update_plugins();
if (argc < 1)
error("expected plugin(s) file name to update");
for (i = 0; i < argc; i++) {
const char *plugin_path = argv[i];
if (!is_wrapper_plugin_0(plugin_path))
error("%s is not a valid nspluginwrapper plugin", plugin_path);
int ret = update_plugin(plugin_path);
if (ret != 0)
return ret;
}
return 0;
}
static int process_install(int argc, char *argv[])
{
int i;
if (g_auto)
return auto_install_plugins();
if (argc < 1)
error("expected plugin(s) file name to install");
for (i = 0; i < argc; i++) {
NPW_PluginInfo plugin_info;
const char *plugin_path = argv[i];
if (!is_compatible_plugin(plugin_path, &plugin_info))
error("%s is not a valid NPAPI plugin", plugin_path);
int ret = install_plugin(plugin_path, &plugin_info);
if (ret != 0)
return ret;
}
return 0;
}
static int process_remove(int argc, char *argv[])
{
int i;
if (g_auto)
return auto_remove_plugins();
if (argc < 1)
error("expected plugin(s) file name to remove");
for (i = 0; i < argc; i++) {
const char *plugin_path = argv[i];
if (!is_wrapper_plugin_0(plugin_path))
error("%s is not a valid nspluginwrapper plugin", plugin_path);
int ret = remove_plugin(plugin_path);
if (ret != 0)
return ret;
}
return 0;
}
int main(int argc, char *argv[])
{
char **args;
int i, j, n_args;
n_args = argc - 1;
args = argv + 1;
if (n_args < 1) {
print_usage();
return 1;
}
if (args[0][0] != '-') {
print_usage();
return 1;
}
static const struct option {
char short_option;
const char *long_option;
int (*process_callback)(int argc, char *argv[]);
bool terminal;
}
options[] = {
{ 'h', "help", process_help, 1 },
{ 'v', "verbose", process_verbose, 0 },
{ 'a', "auto", process_auto, 0 },
{ 'l', "list", process_list, 1 },
{ 'u', "update", process_update, 1 },
{ 'i', "install", process_install, 1 },
{ 'r', "remove", process_remove, 1 },
{ 0, NULL, NULL, 1 }
};
for (i = 0; i < n_args; i++) {
const char *arg = args[i];
const struct option *opt = NULL;
for (j = 0; opt == NULL && options[j].process_callback != NULL; j++) {
if ((arg[0] == '-' && arg[1] == options[j].short_option && arg[2] == '\0') ||
(arg[0] == '-' && arg[1] == '-' && strcmp(&arg[2], options[j].long_option) == 0))
opt = &options[j];
}
if (opt == NULL) {
fprintf(stderr, "invalid option %s\n", arg);
print_usage();
return 1;
}
int ret = opt->process_callback(n_args - i - 1, args + i + 1);
if (opt->terminal)
return ret;
}
return 0;
}