/*
 * Copyright (C) 2002-2017 the xine-project
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 *
 * helper functions to register gxine with kde and gnome
 * (and possibly cde in the future)
 *
 * most code stolen from openoffice
 *
 * oo_1.0.1_src/sysui/oounix/office/gnome
 */

#include "globals.h"

#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>

#include "desktop_integration.h"
#include "utils.h"

#define LIBGNOMEVFS "libgnomevfs-2.so.0"
#define LIBGNOMEUI  "libgnomeui-2.so.0"

typedef enum { GNOME_VFS_OK } GnomeVFSResult; /* incomplete */

/*
 * the bunch of gnome vfs functions needed
 */

struct gnome_vfs_functions
{
  void (*application_registry_remove_application) (const char *app_id);
  void (*application_registry_add_mime_type)	  (const char *app_id, const char *mime_type);
  void (*application_registry_set_value)	  (const char *app_id, const char *key, const char *value);
  void (*application_registry_set_bool_value)	  (const char *app_id, const char *key, int value);
  void (*application_registry_remove_mime_type)	  (const char *app_id, const char *mime_type);
  void (*mime_freeze) (void);
  void (*mime_thaw)   (void);
};

/*
 * the mime info as data structure
 */

struct mime_info
{
  const char * mime_type;
  const char * description;
  const char * extensions;
  const char * icon_filename;
};

/*
 * make the code read as normal gnome app
 */

#define gnome_vfs_application_registry_remove_application gnome_vfs->application_registry_remove_application
#define gnome_vfs_application_registry_add_mime_type gnome_vfs->application_registry_add_mime_type
#define gnome_vfs_application_registry_remove_mime_type gnome_vfs->application_registry_remove_mime_type
#define gnome_vfs_application_registry_set_value gnome_vfs->application_registry_set_value
#define gnome_vfs_application_registry_set_bool_value gnome_vfs->application_registry_set_bool_value

#define gnome_vfs_mime_freeze() do { if (gnome_vfs->mime_freeze) gnome_vfs->mime_freeze (); } while (0)
#define gnome_vfs_mime_thaw()   do { if (gnome_vfs->mime_thaw)   gnome_vfs->mime_thaw ();   } while (0)

static void * gnome_vfs_handle = NULL;
static int gnome_vfs_available = -1;
static struct gnome_vfs_functions * gnome_vfs = NULL;

static void * gnome_ui_handle = NULL;


/*
 * load the GnomeVFS and GnomeUI library
 * If using the integration wizard, also initialize the function pointers
 */

static void *get_func (const char *name)
{
  void *func = dlsym (gnome_vfs_handle, name);
  if (func)
    return func;
  fprintf (stderr, "%s.\n", dlerror());
  return NULL;
}

#define GET_FUNC(VAR,NAME) \
  if (!(*(void **)&(VAR) = get_func ((NAME))))   \
    return gnome_vfs_available = 0

#define GET_FUNC_S(VAR) \
  if (!(*(void **)&(gnome_vfs->VAR) = get_func ("gnome_vfs_" #VAR))) \
    return gnome_vfs_available = 0

#define GET_FUNC_S_I(VAR) \
  *(void **)&(gnome_vfs->VAR) = get_func ("gnome_vfs_" #VAR)

gboolean gxine_vfs_init (void)
{
  void (*init_func) (void) = NULL;

  if (gnome_vfs_available != -1)
    return gnome_vfs_available;

  /* load the gnomevfs library */
  gnome_vfs_handle = dlopen (LIBGNOMEVFS, RTLD_LAZY);
  if (!gnome_vfs_handle)
  {
    fprintf (stderr, "%s.\n", dlerror());
    return gnome_vfs_available = 0;
  }

  gnome_vfs = (struct gnome_vfs_functions *) calloc (1, sizeof (struct gnome_vfs_functions));

  /* this call causes gnome-vfs to become initialized */
  GET_FUNC (init_func, "gnome_vfs_init");
  init_func ();

  /* load the gnomeui library (for the authentication bits) */
  gnome_ui_handle = dlopen (LIBGNOMEUI, RTLD_LAZY);
  if (gnome_ui_handle)
  {
    /* this call causes gnome-vfs to become initialized */
    init_func = dlsym (gnome_ui_handle, "gnome_authentication_manager_init");
    if (init_func)
      init_func ();
    else
      fprintf (stderr, "%s.\n", dlerror());
  }
  else
    fprintf (stderr, "%s.\n", dlerror());

#ifdef USE_INTEGRATION_WIZARD
  void (*gnome_vfs_mime_get_value) (const char *, const char *) = NULL;

  GET_FUNC (gnome_vfs_mime_get_value, "gnome_vfs_mime_get_value");
  gnome_vfs_mime_get_value ("text/plain", "description");

  /* extract needed symbols from the library */
  GET_FUNC_S_I (mime_freeze);
  GET_FUNC_S_I (mime_thaw);
  GET_FUNC_S (application_registry_remove_application);
  GET_FUNC_S (application_registry_add_mime_type);
  GET_FUNC_S (application_registry_set_value);
  GET_FUNC_S (application_registry_set_bool_value);
  GET_FUNC_S (application_registry_remove_mime_type);
#endif

  return gnome_vfs_available = 1;
}

/*
 * release the gnome-vfs library and free the function pointer struct
 */

#undef GET_FUNC
#define GET_FUNC(VAR,NAME) \
  if ((*(void **)&(VAR) = get_func ((NAME))) == NULL)    \
    return

static void di_gnome_vfs_flush (void)
{
  /* release the gnomevfs library */
  if (gnome_vfs_handle)
  {
    void (*gnome_vfs_application_registry_sync) (void) = NULL;

    /* extract and call shutdown function */
    GET_FUNC (gnome_vfs_application_registry_sync, "gnome_vfs_application_registry_sync");

    gnome_vfs_application_registry_sync ();
    /* dlclose (gnome_vfs_handle); */
  }

  free (gnome_vfs);
}


#ifdef USE_INTEGRATION_WIZARD

static char *report = NULL;
static const char *section = NULL;

void di_registration_flush (void)
{
  free (report);
  report = NULL;
  section = NULL;
}

static void __attribute__ ((format (printf, 1, 2)))
di_report (const char *fmt, ...)
{
  char *msg, *markup;
  va_list ap, aq;

  if (section)
  {
    asreprintf (&report, _("%s%s<i>Errors while registering with %s:</i>\n"),
		report ? : "", report ? "\n\n" : "", section);
    section = NULL;
  }

  va_start (ap, fmt);
  va_copy (aq, ap);
  g_printerr (_("gxine desktop integration: "));
  vfprintf (stderr, fmt, ap);
  va_end (ap);
  fprintf (stderr, "\n");
  msg = g_strdup_vprintf (fmt, aq);
  va_end (aq);
  markup = g_markup_printf_escaped ("%s", msg);
  free (msg);
  asreprintf (&report, "%s\n%s", report, markup);
  free (markup);
}

const char *di_registration_report (void)
{
  return report;
}

static FILE *di_fopen (const char *fname)
{
  FILE *f = fopen (fname, "w");
  if (!f)
    di_report (_("can't open file %s for writing: %s"), fname, strerror (errno));
  return f;
}

static void di_fclose (FILE *f, const char *fname)
{
  if (ferror (f))
    di_report (_("error while writing to file %s: %s\n"), fname, strerror (errno));
  if (fclose (f))
    di_report (_("error when closing file %s: %s\n"), fname, strerror (errno));
}


/*
 *  add the installed application to the gnome application info registry
 */
static GnomeVFSResult
register_application (const char *application_id, const char *product_name,
		      const char *product_version, const char *command,
		      const char *install_path)
{
  char *command_full = (char *) alloca (strlen (install_path) + strlen (command) + 10);
  char *product = (char *) alloca (strlen (product_name) + strlen (product_version) + 2);

  sprintf (command_full, "%s/%s", install_path, command);
  sprintf (product, "%s %s", product_name, product_version);

  /* entry will be created automatically */
  gnome_vfs_application_registry_set_value (application_id, "name", product);
  gnome_vfs_application_registry_set_value (application_id, "command", command_full);
  gnome_vfs_application_registry_set_bool_value (application_id, "can_open_multiple_files", 1);
  gnome_vfs_application_registry_set_bool_value (application_id, "requires_terminal", 0);

  return GNOME_VFS_OK;
}

void di_register_gnome (void)
{
  char *mime_types;
  char *freeme;

  section = "GNOME";

  if (!gxine_vfs_init())
  {
    di_report (_("GNOME initialisation failed"));
    return;
  }

  /* freeze the lists to enable multiple updates */
  gnome_vfs_mime_freeze();

  register_application ("gxine", "gxine", VERSION, "gxine", bindir);
  /*
   * iterate over all mime types xine supports
   * and register it as a handler for each of them
   */

  freeme = mime_types = xine_get_mime_types (xine);
  while (mime_types)
  {
    char *next = mime_types;

    if ((next = strchr (mime_types, ';')))
    {
      *next=0;
      char *end = strchr (mime_types, ':');
      if (end)
      {
	*end=0;
	g_print (_("desktop_integration: registering xine for MIME type ‘%s’\n"),
		mime_types);
	gnome_vfs_application_registry_add_mime_type ("gxine", mime_types);
      }
    }

    mime_types = next ? next + 1 : NULL;
  }

  free(freeme);

  /* force the user.mime and user.keys to be written  */
  gnome_vfs_mime_thaw();

  di_gnome_vfs_flush();
}

void di_register_kde (void)
{
  gchar *fname;
  gchar *kdehome;
  FILE  *f;
  char  *mime_types;
  char  *freeme;

  section = "KDE";

  kdehome = getenv ("KDEHOME");

  if (kdehome)
    fname = g_build_filename (kdehome, "share/applnk/Multimedia", NULL);
  else
    fname = g_build_filename (g_get_home_dir(), ".kde/share/applnk/Multimedia", NULL);
  if (ensure_path_exists (fname, 0755))
  {
    di_report (_("can't create directory %s: %s"), fname, strerror (errno));
    g_free (fname);
    return;
  }
  g_free (fname);

  /*
   * produce gxine.desktop
   */

  if (kdehome)
    fname = g_build_filename (kdehome, "share/applnk/Multimedia", "gxine.desktop", NULL);
  else
    fname = g_build_filename (g_get_home_dir(), ".kde/share/applnk/Multimedia", "gxine.desktop", NULL);

  f = di_fopen (fname);
  if (!f)
  {
    g_free (fname);
    return;
  }

  fprintf (f, "[Desktop Entry]\n"
	   "Version=1.0\n"
	   "Encoding=UTF-8\n"
	   "Name=gxine\n"
	   "GenericName=Video Player\n"
	   "Comment=Play films and songs, or watch digital TV\n"
	   "MultipleArgs=true\n"
	   "Terminal=false\n"
	   "Icon=gxine\n"
	   "Exec=%s/gxine\n"
	   "Type=Application\n"
	   "StartupNotify=true\n"
	   "Categories=GTK;Application;AudioVideo;\n"
	   "MimeType=",
	   bindir);

  /*
   * iterate over all mime types xine supports
   * and register it as a handler for each of them
   */

  freeme = mime_types = xine_get_mime_types (xine);
  while (mime_types)
  {
    char *next = mime_types;

    if ((next = strchr (mime_types, ';')))
    {
      *next=0;
      char *end = strchr (mime_types, ':');
      if (end)
      {
	*end=0;
	g_print (_("desktop_integration: KDE registering xine for MIME type ‘%s’\n"),
		mime_types);
	fprintf (f, "%s;", mime_types);
      }
    }

    mime_types = next ? next + 1 : NULL;
  }
  free(freeme);

  fprintf (f, "\n");
  di_fclose (f, fname);
  g_free (fname);
}

void di_register_mailcap (void)
{
  char  *mc;
  gchar *fname;
  ssize_t n;
  FILE  *f;
  char  *mime_types;
  char  *freeme;

  section = "mailcap";

  fname = g_build_filename (g_get_home_dir(), ".mailcap", NULL);

  mc = read_entire_file (fname, &n);

  f = di_fopen (fname);
  if (!f)
  {
    free (mc);
    g_free (fname);
    return;
  }

  if (mc)
    fwrite (mc, n, 1, f);

  /*
   * iterate over all mime types xine supports
   * and register it as a handler for each of them
   */

  freeme = mime_types = xine_get_mime_types (xine);
  while (mime_types)
  {
    char *next = mime_types;

    if ((next = strchr (mime_types, ';')))
    {
      *next=0;
      char *end = strchr (mime_types, ':');
      if (end)
      {
	*end=0;
	if (!mc || !strstr (mc, mime_types))
	{
	  g_print (_("desktop_integration: mailcap: registering xine for MIME type ‘%s’\n"),
		  mime_types);

	  fprintf (f, "%s;%s/gxine %%s\n", mime_types,
		   bindir);
	}
	else /* MIME type already registered */
	  g_print (_("desktop_integration: %s already has a handler\n"),
		  mime_types);
      }
    }

    mime_types = next ? next + 1 : NULL;
  }

  fprintf (f, "\n");
  di_fclose (f, fname);
  g_free (fname);
  free (mc);
  free(freeme);
}

void di_register_mozilla (void)
{
  gchar *fname, *sname;
  char buf[FILENAME_MAX];
  unsigned int i;
  struct stat st;

  static const char *dir[] = { ".mozilla", ".mozilla/firefox", ".firefox" };

  section = "Mozilla";

  sname = g_build_filename (plugindir, "gxineplugin.so", NULL);
  if (stat (sname, &st))
    return;

  g_print (_("wizards: installing browser plugin...\n"));

  for (i = 0; i < G_N_ELEMENTS (dir); ++i)
  {
    /* just make sure the directory exists */
    fname = g_build_filename (g_get_home_dir(), dir[i], "plugins", NULL);
    if (ensure_path_exists (fname, 0755))
    {
      di_report (_("can't create directory %s: %s"), fname, strerror (errno));
      goto next;
    }
    g_free (fname);

    fname = g_build_filename (g_get_home_dir(), dir[i], "plugins/gxineplugin.so", NULL);
    buf[0] = 0;

    /* If the destination object exists, check if it's a symlink and if it's
     * pointing to the right place. If any of those are not true, we unlink
     * the object (if it exists) and create a fresh symlink in its place.
     */

    if (readlink (fname, buf, sizeof (buf)) == -1 &&
	errno != EINVAL && errno != ENOENT)
    {
      di_report (_("symlink check on %s failed: %s"), fname, strerror (errno));
      goto next;
    }

    if (!strcmp (buf, sname))
      goto next;

    if (unlink (fname) && errno != ENOENT)
      di_report (_("cannot unlink %s: %s"), fname, strerror (errno));

    if (symlink (sname, fname) && errno != EEXIST)
      di_report (_("cannot link %s to %s: %s"), sname, fname, strerror (errno));

    next:
    g_free (fname);
  }

  g_free (sname);
}

#endif /* USE_INTEGRATION_WIZARD */
