/***************************************************************************
                          linkhandler.c  -  description
                             -------------------
    begin                : Fri Jan 17 2003
    copyright            : (C) 2003 by Tim-Philipp Müller
    email                : t.i.m@orange.net
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

/*  The code in here is called from main() when we are called with
 *   an ed2k-link as the first command line argument.
 *
 *  In this case we don't want a GUI instance to pop up, but
 *   only a separate little window that passes the link on to
 *   an already running GUI and then goes away.
 *
 *  Unless, of course, there is no GUI running yet. Then we need
 *   to start one and pass the link to the core after connect.
 *
 */

#include "icons.h"
#include "core-conn.h"
#include "linkhandler.h"
#include "mainwindow.h"
#include "misc.h"
#include "misc_gtk.h"
#include "misc_sys.h"
#include "misc_strings.h"
#include "options.h"

#include "status_page.h"

#if defined(G_OS_UNIX)
# include <sys/types.h>
# include <sys/wait.h>	/* for the return code from system() */
# include <sys/stat.h>
# include <unistd.h>
#endif

#if defined(G_OS_WIN32)
# include <Windows.h>
# include <direct.h>
#endif

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <gtk/gtk.h>


/* local variables */

static GSList       *lh_pipes_found;                 /* NULL */
static gchar        *lh_ed2klink_utf8;               /* NULL */
static gchar       **lh_pending_links_array;         /* NULL */
static GtkWidget    *lh_entry;                       /* NULL */
static GtkWidget    *lh_cb_dlnow;                    /* NULL */
static GtkWidget    *lh_window;                      /* NULL */
static GtkWidget    *lh_statuslabel;                 /* NULL */
static GtkWidget    *lh_cb_default_action;           /* NULL */
static const gchar  *lh_error_msg;                   /* NULL */
static const gchar  *lh_prio_suffix;                 /* NULL */
static gboolean      lh_main_run_gui_override_flag;  /* FALSE */

/* local functions */

#if defined(G_OS_UNIX)
static void       linkhandler_remove_stale_pipes(void);
static gboolean   linkhandler_remove_stale_pipes_is_options_file_bad(const gchar *pipename, time_t pipe_mtime);
static gint       linkhandler_check_for_existing_GUI_instances (void);
#elif defined(G_OS_WIN32)
static gint       linkhandler_check_for_existing_GUI_instances (void);
#endif

static gboolean   linkhandler_send_link_to_pipe (const gchar *pipename, const gchar *link);
static void       linkhandler_send_link_to_all_pipes (const gchar *link);

static void       linkhandler_ask_buttons_callback (GtkWidget *widget, gpointer data);
static void       linkhandler_instance_button_callback (GtkWidget *widget, gchar *pipepath);
static GtkWidget *linkhandler_create_buttons (void);
static gint       linkhandler_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data);
static void       linkhandler_destroy (GtkWidget *widget, gpointer data);
static void       linkhandler_create_mainwindow (const gchar *ed2klink_utf8);

static gboolean   linkhandler_append_link_to_pending_file (const gchar *link);

static gboolean   linkhandler_check_for_pending_links_callback (GtkWidget *dialog);

#if !defined(G_OS_UNIX)

const gchar  *
linkhandler_install_kde (void)
{
	return _("Not supported in Windows or BEOS.");
}

const gchar *
linkhandler_install_gnome1 (void)
{
	return _("Not supported in Windows or BEOS.");
}

const gchar *
linkhandler_install_gnome2 (void)
{
	return _("Not supported in Windows or BEOS.");
}

#else

/******************************************************************************
 *
 *   linkhandler_install_kde
 *
 *   installs the GUI as linkhandler for ed2k://-links in KDE
 *   (adds a file ed2k.protocol in ~/share/services/)
 *
 *   returns error string on failure, and NULL on success
 */

const gchar *
linkhandler_install_kde (void)
{
	FILE         *cfgfile;
	const gchar  *home;
	gchar        *cfgpath;
	gchar        *cfgfn;
	gchar        *binpath;

	home = g_getenv("HOME");
	g_return_val_if_fail ( home != NULL, "home==NULL?!");

	cfgpath = g_strdup_printf ("%s/.kde/share/services/", home);
	g_return_val_if_fail (cfgpath != NULL, "cfgpath==NULL?!");

	if ( access(cfgpath,X_OK|W_OK) != 0 )
	{
		g_free(cfgpath);

		switch(errno)
		{
			case ENOENT:
				return _("$HOME/.kde/share/services/ does not exist?!");
			case EACCES:
				return _("$HOME/.kde/share/services/ is not writable?!");
			default:
				return _("$HOME/.kde/share/services/ can't be accessed.");
		}
	}

	cfgfn = g_strdup_printf ( "%s/ed2k.protocol", cfgpath);
	g_return_val_if_fail ( cfgfn != NULL, "cfgfn==NULL?!" );

	cfgfile = fopen (cfgfn, "w");
	g_return_val_if_fail ( cfgfile != NULL, _("could not open $HOME/.kde/share/services/ed2k.protocol for writing?!"));

	/* If the GUI binary is going to be installed into a 'non-standard'
	 * path, specify the absolute path here, in case that path is not in
	 * $PATH (is that a Good Thing(tm) to do?! TheUnixAdmin thinks so...) */
	binpath = g_strdup(LOCALEPATH);
	if (    strncmp(LOCALEPATH,"/usr/share",10)       != 0  /* prefix is not a 'normal' prefix and LOCALEPATH       */
	     && strncmp(LOCALEPATH,"/usr/local/share",16) != 0  /* is composed how we expect it (prefix+locale-postfix) */
	     && strcmp(LOCALEPATH+strlen(LOCALEPATH)-13,"share/locale/") == 0 ) /* 13 = strlen("share/locale/")         */
	{
		binpath[strlen(LOCALEPATH)-13] = 0x00;
	}
	else
		binpath[0] = 0x00; /* do not specify the path */

	fprintf(cfgfile, "[Protocol]\n");
	fprintf(cfgfile, "exec=%sed2k_gui %%u\n", binpath);
	fprintf(cfgfile, "protocol=ed2k\n");
	fprintf(cfgfile, "input=none\n");
	fprintf(cfgfile, "output=none\n");
	fprintf(cfgfile, "helper=true\n");
	fprintf(cfgfile, "listing=\n");
	fprintf(cfgfile, "reading=false\n");
	fprintf(cfgfile, "writing=false\n");
	fprintf(cfgfile, "makedir=false\n");
	fprintf(cfgfile, "deleting=false\n");
	fprintf(cfgfile, "Icon=remote\n");
	fprintf(cfgfile, "Description=ed2k-gtk-gui handles eDonkey2000 ed2k://-links\n");
	fclose(cfgfile);

	status_message_blue (_("GUI: created new file ~/.kde/share/services/ed2k.protocol\n"));

	g_free(cfgfn);
	g_free(cfgpath);
	g_free(binpath);

	return NULL;
}


/******************************************************************************
 *
 *   linkhandler_install_gnome1
 *
 *   installs the GUI as linkhandler for ed2k://-links in GNOME 1
 *
 *   returns error string on failure, and NULL on success
 */

const gchar *
linkhandler_install_gnome1 (void)
{
	gchar         *gnomefilepath;
	gchar        **gnomefilebuf;
	gchar         *gnomefilefn;
	const gchar   *home;
	const gchar   *linkstring = "ed2k-show=ed2k_gui \"%s\"";
	gint           i;

	home = g_getenv("HOME");
	g_return_val_if_fail ( home != NULL, "home==NULL?!");

	gnomefilepath = g_strdup_printf("%s/.gnome/", home);
	g_return_val_if_fail ( gnomefilepath != NULL, "gnomefilepath==NULL?!");

	if ( access(gnomefilepath, X_OK|W_OK) != 0 )
	{
		g_free(gnomefilepath);

		switch(errno)
		{
			case ENOENT:
				return (_("$HOME/.gnome does not exist ?!"));
			case EACCES:
				return (_("$HOME/.gnome is not writable ?!"));
			default:
				return (_("$HOME/.gnome cannot be accessed."));
		}
	}

	gnomefilefn = g_strdup_printf( "%sGnome", gnomefilepath);
	g_return_val_if_fail( gnomefilefn != NULL, "gnomefilefn=NULL?!" );

	gnomefilebuf = misc_get_array_of_lines_from_textfile(gnomefilefn, FALSE, FALSE);

	/* If file does not yet exist or is empty
	 *   => create it with [URL Handlers] section
	 *
	 * If file does already exist
	 *   => (1) check whether [URL Handlers] section exists
	 *           If no => add section
	 *      (2) add ed2k handler in this section and remove all existing ed2k-handlers
	 *
	 */
	if ((!gnomefilebuf) || (!*gnomefilebuf))
	{
		G_FREE(gnomefilebuf);

		gnomefilebuf = g_new0(gchar*, 2);

		gnomefilebuf[0] = g_strdup_printf("[URL Handlers]\n%s", linkstring);
		g_return_val_if_fail(gnomefilebuf[0]!=NULL, "gnomefilebuf[0]==NULL?!");

		gnomefilebuf[1] = NULL;

	}
	else
	{

		gint handler_section_line = -1;	/* line in which it exists; -1 = does not exist */
		gint linesinfile = 0;

		/* Walk through lines of file.
		 *  - count number of lines
		 *  - check whether we already have an 'URL Handlers' section and where it is
		 *  - remove existing ed2k handlers
		 */

		for (i = 0; gnomefilebuf[i]!=NULL; i++)
		{
			linesinfile++;

			/* remember where 'URL Handlers' section starts */
			if (strstr(gnomefilebuf[i], "[URL Handlers]"))
				handler_section_line = i;

			/* replace existing ed2k-handlers by empty line */
			if (strstr(gnomefilebuf[i], "ed2k-show"))
			{
				g_free(gnomefilebuf[i]);
				gnomefilebuf[i] = g_strdup("SkIpThIsLiNeDoNoTwRiTeIt");
			}
		}

		/* If section does not exist yet
		 *   => add section + handler at the end, so we don't
		 *      screw up stuff before that does not belong to
		 *      URL Handler section
		 *
		 * If section exists, add new ed2k-handler first
		 *   thing after section header (we have already
		 *   removed all previous ed2k handlers, so this
		 *   should be fine)
		 *
		 */
		if (handler_section_line<0)
		{
			gchar *oldlastline = gnomefilebuf[linesinfile-1];
			gnomefilebuf[linesinfile-1] = g_strdup_printf("%s\n[URL Handlers]\n%s",oldlastline,linkstring);
			g_free(oldlastline);
		} else {
			g_free(gnomefilebuf[handler_section_line]);
			gnomefilebuf[handler_section_line] = g_strdup_printf("[URL Handlers]\n%s",linkstring);
		}
	}

	/* gnomefilebuf should really be set
	 *   here, but better check again
	 *
	 * write new ~/.gnome/Gnome file to disk
	 *
	 */
	if (gnomefilebuf)
	{
		FILE *gnomefile;

		gnomefile = fopen(gnomefilefn, "w");
		if (!gnomefile)
		{
			g_strfreev(gnomefilebuf);
			g_return_val_if_fail(gnomefile != NULL, _("couldn't open $HOME/.gnome/Gnome for writing ?!"));
		}

		for (i = 0; gnomefilebuf[i] != NULL; i++)
		{
			if (strcmp(gnomefilebuf[i], "SkIpThIsLiNeDoNoTwRiTeIt" )!=0)
			{
				fprintf(gnomefile, "%s\n", gnomefilebuf[i]);
			}
		}

		g_strfreev(gnomefilebuf);
		fclose(gnomefile);
	}
	else
		return "gnomefilebuf==NULL?!";

	g_free(gnomefilepath);
	g_free(gnomefilefn);

	status_message_blue (_("GUI: modified file ~/.gnome/Gnome\n"));

	return NULL;
}


/******************************************************************************
 *
 *   linkhandler_install_gnome2
 *
 *   installs the GUI as linkhandler for ed2k://-links in GNOME 2
 *
 *   returns error string on failure, and NULL on success
 */

const gchar *
linkhandler_install_gnome2 (void)
{
	const gchar *scriptfilename;
	gint         ret;

	scriptfilename = opt_get_opt_filename_without_instance("gnome2_ed2k_url_handler_installer.sh");

	g_return_val_if_fail ( scriptfilename != NULL, "[FIXME][linkhandler_install_gnome2]");

	/* If the script already exists, overwrite 
	 *  it with this (possibly newer) version */
	{
		FILE *of;

		of = fopen (scriptfilename, "w");
		if (!of)
			return _("Could not create URL handler installer script in config path!?");

		status_message_blue ("GUI: Creating URL handler installer script '%s'...\n", scriptfilename);

		fprintf (of, "#!/bin/sh\n#\n# This little script installs the ed2k_gui as default handler\n");
		fprintf (of, "#  for ed2k:// URLs in the GNOME2 desktop environment\n#\n\n");
		fprintf (of, "echo\necho \"--- Trying to install ed2k link handler for GNOME2 ---\"\necho\n");
		fprintf (of, "echo -n -e \"Testing for gconftool-2 ...\t\"\n\n");
		fprintf (of, "GCONFTOOL2=`which gconftool-2`\n\n");

		fprintf (of, "if [ ! -x \"$GCONFTOOL2\" ]\nthen\n");
		fprintf (of, "echo -e \"not found.\n\nAre you sure you are using GNOME2?\"\nexit 1;\n");
		fprintf (of, "else\necho \"ok ($GCONFTOOL2)\"\nfi\n\n");
		fprintf (of, "echo -n -e \"Setting up keys with gconftool-2 ...\t\"\n");
		fprintf (of, "if $GCONFTOOL2 --recursive-unset /desktop/gnome/url-handlers/ed2k\n");
		fprintf (of, "then\n if $GCONFTOOL2 --type=string --set /desktop/gnome/url-handlers/ed2k/description \"eDonkey-link\"\n");
		fprintf (of, " then\n  if $GCONFTOOL2 --type=string --set /desktop/gnome/url-handlers/ed2k/command 'ed2k_gui \"%%s\"'\n");
		fprintf (of, "  then\n   if $GCONFTOOL2 --type=boolean --set /desktop/gnome/url-handlers/ed2k/type true\n");
		fprintf (of, "   then\n   if $GCONFTOOL2 --type=boolean --set /desktop/gnome/url-handlers/ed2k/enabled true\n");
		fprintf (of, "    then\n   if $GCONFTOOL2 --type=boolean --set /desktop/gnome/url-handlers/ed2k/needs_terminal false\n");
		fprintf (of, "     then\n    echo -e \"ok\n\nDone.\n\"\n    exit 0\n    fi\n   fi\n  fi\n fi\nfi\nfi\n");
		fprintf (of, "echo -e \"failed\n\n\"\nexit 2;\n");

		/* make script executable */
		if (fchmod(fileno(of), S_IRUSR|S_IWUSR|S_IXUSR)<0)
		{
			fclose(of);
			return _("Couldn't make script executable!\n");
		}
		fclose(of);
		status_message_blue (_("GUI: installer script '%s' created. Trying to run it now.\n"), scriptfilename);
	}

	ret = system(scriptfilename);

	if (ret == -1)
		return "fork() in system() failed.";

	if (WEXITSTATUS(ret) == 0)
		return NULL;

	if (WEXITSTATUS(ret) == 1)
		return _("gconftool-2 program not found. Are you sure you are using GNOME 2?");

	if (WEXITSTATUS(ret) == 2)
		return _("Something went wrong when installing the configuration keys with\n"
		         "  gconftool-2. Run the script manually to see what went wrong. ");

	return "[FIXME][linkhandler_install_gnome2]";
}

#endif


#if defined(G_OS_WIN32)

/******************************************************************************
 *
 *   linkhandler_install_win32
 *
 *   installs the GUI as linkhandler for ed2k://-links in Windows
 *
 *   returns error string on failure, and NULL on success
 *
 ******************************************************************************/

const gchar *
linkhandler_install_windows (void)
{
	HKEY	key;
	gchar	*path,*exec;
	gchar	*v1,*v2;
	gchar	*err;

	path = g_path_get_dirname(_argv[0]);  
	exec = g_path_get_basename(_argv[0]); 
	
	if( path && !g_path_is_absolute(path) ) {
	    v2 = NULL;
	    
	    v1 = g_get_current_dir();
	    if( v1 ) v2=g_strdup_printf( "%s\\%s", v1, path );
	    
	    G_FREE(path);	    
	    G_FREE(v1);	    
	    path=v2;
	}
	
	v1   = NULL;		
	if( !path || !exec ) { err=_("getting executable's name failed"); goto err_hand; }

	

	if( RegCreateKeyEx( HKEY_CLASSES_ROOT, "ed2k",
			0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL )!=ERROR_SUCCESS ) {
	    err=_("Couldn't create 'ed2k' registry entry");
	    goto err_hand;
	}

	v2="URL: ed2k Protocol";
	if( RegSetValueEx( key, NULL, 0, REG_SZ, v2, strlen(v2)+1 )!=ERROR_SUCCESS ) {
	    err=_("Couldn't set 'ed2k\\Default' registry entry"); 
	    goto err_key;
	}

	if( RegSetValueEx( key, "URL Protocol", 0, REG_SZ, "", 1 )!=ERROR_SUCCESS ) {
	    err=_("Couldn't set 'ed2k\\URL Protocol' registry entry"); 
	    goto err_key;
	}

	RegCloseKey(key);



	if( RegCreateKeyEx( HKEY_CLASSES_ROOT, "ed2k\\DefaultIcon",
			0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL )!=ERROR_SUCCESS ) {
	    err=_("Couldn't create 'ed2k\\DefaultIcon' registry entry");
	    goto err_hand;
	}

	v1 = g_strdup_printf("%s\\%s", path, exec);
	if( !v1 || RegSetValueEx( key, NULL, 0, REG_SZ, v1, strlen(v1)+1 )!=ERROR_SUCCESS ) {
	    err=_("Couldn't set 'ed2k\\DefaultIcon\\Default' registry entry");
	    goto err_key;
	}

	G_FREE(v1);
	RegCloseKey(key);
	


	if( RegCreateKeyEx( HKEY_CLASSES_ROOT, "ed2k\\shell",
			0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL )!=ERROR_SUCCESS ) {
	    err=_("Couldn't create 'ed2k\\shell' registry entry");
	    goto err_hand;
	}

	v2="open";
	if( RegSetValueEx( key, NULL, 0, REG_SZ, v2, strlen(v2)+1 )!=ERROR_SUCCESS ) {
	    err=_("Couldn't set 'ed2k\\shell\\Default' registry entry");
	    goto err_key;
	}

	RegCloseKey(key);


	if( RegCreateKeyEx( HKEY_CLASSES_ROOT, "ed2k\\shell\\open\\command",
			0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL )!=ERROR_SUCCESS ) {
	    err=_("Couldn't create 'ed2k\\shell\\open\\command' registry entry");
	    goto err_hand;
	}

	v1 = g_strdup_printf("%s\\gspawn-win32-helper.exe 2 z z z \"%s\" y n n \"%s\\%s\" \"%%1\"", 
				    path, path, path, exec );
	
	if( !v1 || RegSetValueEx( key, NULL, 0, REG_SZ, v1, strlen(v1)+1 )!=ERROR_SUCCESS ) {
	    err=_("Couldn't set 'ed2k\\shell\\open\\command\\Default' registry entry");
	    goto err_key;
	}

	err=NULL;

    err_key:
	RegCloseKey(key);

    err_hand:		
	G_FREE(path);
	G_FREE(exec);
	G_FREE(v1);
		
	return err;
}

#else

const gchar *
linkhandler_install_windows (void)
{
	return _("Not supported in Unix or BEOS");
}


#endif


#if defined(G_OS_UNIX)

/******************************************************************************
 *
 *   linkhandler_remove_stale_pipes_is_options_file_bad
 *
 *   checks whether the options file corresponding to a pipe has
 *    been changed recently or exists at all.
 *
 *   Returns TRUE if the pipe is probably stale (options file not changed),
 *    and FALSE if the pipe might be in use.
 *
 */

static gboolean
linkhandler_remove_stale_pipes_is_options_file_bad(const gchar *pipename, time_t pipe_mtime)
{
	struct stat   optionsfile_status;
	gchar        *basename, *dirname, *optionsname;
	gint          ret;

	g_return_val_if_fail ( pipename != NULL, FALSE );

	basename = g_path_get_basename(pipename);
	g_return_val_if_fail ( basename != NULL, FALSE );

	dirname = g_path_get_dirname(pipename);
	g_return_val_if_fail ( dirname != NULL, FALSE );

	optionsname = g_strdup_printf("%s/options%s",dirname, (strcmp(basename,"pipe")==0) ? "" : basename+4);
	g_return_val_if_fail ( optionsname != NULL, FALSE );

	memset(&optionsfile_status,0x00,sizeof(optionsfile_status));

	ret = stat(optionsname, &optionsfile_status);

	g_free(optionsname);
	g_free(dirname);
	g_free(basename);

	/* stat() failed? if options file does not exist, check
	 *  whether pipe was created a long time ago, and
	 *  an options file should exist
	 */
	if (ret<0)
		return ((errno==ENOENT) && pipe_mtime < (time(NULL)-(60*60)));

	return (optionsfile_status.st_mtime < (time(NULL)-(60*60)));
}



/******************************************************************************
 *
 *   linkhandler_remove_stale_pipes
 *
 *   removes pipes that have existed longer than the system was up
 *    => those are definitively stale
 *
 */

static void
linkhandler_remove_stale_pipes(void)
{
	time_t	 uptime, system_start;
	GSList	*node;

	uptime = misc_sys_get_uptime_in_secs();

	if (uptime==0)
		return;

	system_start = time(NULL)-uptime;

	node = lh_pipes_found;
	while(node)
	{
		if ((node)&&(node->data))
		{
			struct stat pipe_status;
			memset(&pipe_status,0x00,sizeof(pipe_status));
			if (stat((gchar*)node->data, &pipe_status)==0)
			{
				gboolean	 remove_it = FALSE;

				/* (1) Was pipe last modified/created before last system start?
				 *      If yes => it's stale. Remove it.
				 *
				 * (2) Was the 'options' file that belongs to the GUI instance that
				 *      owns the pipe modified in the last 60 minutes?
				 *      If no, then GUI is not running, and pipe is stale => Remove it.
				 *
				 */

				if (pipe_status.st_mtime < system_start)
				{
					remove_it = TRUE;
				} else
				{
					if (linkhandler_remove_stale_pipes_is_options_file_bad((gchar*)node->data, pipe_status.st_mtime))
					{
						remove_it = TRUE;
					}
				}

				if (remove_it)
				{
					GSList *oldnode = node;
					gint    ulret;


					ulret = unlink((gchar*)oldnode->data);

					if (ulret == 0)
					{
						g_print ("removed stale pipe: %s\n", (gchar*)oldnode->data);
					}
					else
					{
						g_print ("could not remove stale pipe: %s - %s\n", (gchar*)oldnode->data, g_strerror(errno));
					}

					node = node->next;
					lh_pipes_found = g_slist_remove_link(lh_pipes_found, oldnode);
					G_FREE(oldnode->data);
					g_slist_free_1(oldnode);
					continue;
				}

			}
		}
		node=node->next;
	}
}

/******************************************************************************
 *
 *   linkhandler_check_for_existing_GUI_instances
 *
 *   checks whether there is already one or more GUIs running.
 *
 *   Returns number of GUI instances found, or -1 on error.
 *
 */

static gint
linkhandler_check_for_existing_GUI_instances (void)
{
	const gchar   *prefdir, *entryname;
	GError        *error = NULL;
	GDir          *directoryhandle;

	prefdir = opt_get_opt_filename_without_instance("");

	directoryhandle = g_dir_open(prefdir, 0, &error);

	if (error)
	{
		g_printerr ("g_dir_open(%s) failed - %s\n", prefdir, error->message);
		g_error_free(error);
		return -1;
	}

	/* remove and free existing, previously
	 *  found results if there are any */
	if (lh_pipes_found)
	{
		GSList *node = lh_pipes_found;
		while (node)
		{
			G_FREE(node->data);	/* previously allocated with g_strdup(pipe_path) */
			node = node->next;
		}
	}

	while ((entryname=g_dir_read_name(directoryhandle)))
	{
		gchar *fullpath;

		if (strncmp(entryname,"pipe",4) != 0)
			continue;

		fullpath = g_strconcat (prefdir, G_DIR_SEPARATOR_S, entryname, NULL);
		if (fullpath)
		{
				lh_pipes_found = g_slist_append(lh_pipes_found, fullpath);
/*				g_print (">>>>>>>>> found: '%s'\n", fullpath); */
		}
	}

	g_dir_close(directoryhandle);

	linkhandler_remove_stale_pipes();

	return (gint) g_slist_length(lh_pipes_found);
}

/******************************************************************************
 *
 *   linkhandler_send_link_to_pipe
 *
 *   sends link to named pipe pipename
 *
 *   Returns TRUE on success
 *
 */

static gboolean
linkhandler_send_link_to_pipe(const gchar *pipename, const gchar *link)
{
	FILE *outpipe;
	gint  len, r;

	g_return_val_if_fail (pipename != NULL, FALSE);
	g_return_val_if_fail (link     != NULL, FALSE);

	outpipe = fopen(pipename, "w");

	if(outpipe == NULL)
		return FALSE;

	len = strlen(link)+1;
	r = fwrite((void*)link, len, 1, outpipe);
	fclose(outpipe);

	return (r != 0);
}

#elif defined(G_OS_WIN32)

/******************************************************************************
 *
 *   linkhandler_check_for_existing_GUI_instances
 *
 *   checks whether there is already one or more GUIs running.
 *
 *   Returns number of GUI instances found, or -1 on error.
 *
 */

static gint
linkhandler_check_for_existing_GUI_instances (void)
{
	WIN32_FIND_DATA	FileData;
	HANDLE		hSearch;
	gchar   	*pname,*mask;
	gint		len;

	/* remove and free existing, previously
	 *  found results if there are any */
	if (lh_pipes_found)
	{
		GSList *node = lh_pipes_found;
		while (node)
		{
			G_FREE(node->data);     /* previously allocated with g_strdup(pipe_path) */
			node = node->next;
		}
	}

	/* mask '\\\\.\\\pipe\\ed2k-gtk-gui*' doesn't work 
	 * that's why we have to later parse name*/
	hSearch = FindFirstFile(  "\\\\.\\pipe\\*", &FileData );
	if( hSearch==INVALID_HANDLE_VALUE ) {
	    return 0;
	}
	
	mask = "ed2k-gtk-gui\\pipe";
	len  = strlen(mask);
	
	while( TRUE ) {
	    if( strncmp(mask,FileData.cFileName,len)==0 ) {
		pname = g_strdup_printf( "\\\\.\\pipe\\%s", FileData.cFileName );
		if( pname ) lh_pipes_found = g_slist_append(lh_pipes_found, pname);
	    }
	    	    
	    if( !FindNextFile( hSearch, &FileData ) ) break;
	}
	
	FindClose(hSearch);
	
	return (gint) g_slist_length(lh_pipes_found);
}

/******************************************************************************
 *
 *   linkhandler_send_link_to_pipe
 *
 *   sends link to named pipe pipename
 *
 *   Returns TRUE on success
 *
 */


static gboolean
linkhandler_send_link_to_pipe( const gchar* pipename, const gchar* link )
{
	gint   len;
	DWORD  r,b,v;

	len = strlen(link)+1;
	r = CallNamedPipe(pipename, (void*)link, len, &b, sizeof(b), &v, 5000);

	return (r != 0);
}

#else /* not unix, not win32 */

static gboolean
linkhandler_send_link_to_pipe( const gchar* pipename, const gchar* link )
{
    g_return_val_if_reached(TRUE);
}

#endif


/******************************************************************************
 *
 *   linkhandler_instance_button_callback
 *
 ******************************************************************************/

static void
linkhandler_instance_button_callback (GtkWidget *widget, gchar *pipepath)
{
	const gchar *errmsg = NULL;
	gchar       *name = NULL;
	gchar       *hashstr = NULL;
	gchar       *editedname = NULL;
	guint        size = 0;

	g_return_if_fail (pipepath != NULL);
	g_return_if_fail (lh_entry != NULL);
		
	editedname = gtk_editable_get_chars (GTK_EDITABLE (lh_entry), 0, -1);
	
	/* link should still be valid, but better check again */
	/* TODO: make sure OK buttons are all insensitive 
	 *       as long as the filename is empty */
	if (editedname && *editedname)
	{
		if (misc_check_if_ed2klink_is_valid(lh_ed2klink_utf8,&name,&size,&hashstr,NULL,NULL))
		{
			if ((name)&&(hashstr)&&(size>0))
			{
				GString *linkstr;
						
				linkstr = g_string_new ("ed2k://|file|");
				
				g_strdelimit (editedname, "|\"", '_');

				g_string_append_printf (linkstr, "%s|%u|%s|", editedname, size, hashstr);
						
				if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (lh_cb_dlnow)))
					g_string_append (linkstr, "pause|");

				if (lh_prio_suffix)
					g_string_append (linkstr, lh_prio_suffix);

				g_string_append (linkstr, "/\n\n");

				if(!linkhandler_send_link_to_pipe(pipepath, linkstr->str))
					errmsg = _(" Could not send link to pipe.");

				g_string_free (linkstr, TRUE);

			} else errmsg = "link ok, but fields bad ?!";
		} else errmsg = "link turned sour ?!";
	} else errmsg = "editedname==NULL ?!";

	G_FREE(pipepath);	/* was a g_strdup() of the pipe path */
	G_FREE(name);
	G_FREE(hashstr);
	G_FREE(editedname);

	if (!errmsg)
	{
		gtk_widget_destroy(lh_window);
		return;
	}

	g_return_if_fail (lh_statuslabel!=NULL);

	gtk_label_set_text(GTK_LABEL(lh_statuslabel), UTF8_SHORT_PRINTF("%s", errmsg));
	gtk_widget_show(lh_statuslabel);
	gtk_widget_grab_focus (lh_entry);
}


/******************************************************************************
 *
 *   linkhandler_remember_link_button_callback
 *
 *
 ******************************************************************************/

static void
linkhandler_ask_buttons_callback (GtkWidget *widget, gpointer data)
{
	const gchar *errmsg = NULL;
	gchar       *name = NULL;
	gchar       *hashstr = NULL;
	gchar       *editedname = NULL;
	guint        size = 0;
	guint        action = GPOINTER_TO_UINT(data);

	if (lh_entry)
	{
		editedname = gtk_editable_get_chars (GTK_EDITABLE(lh_entry),0,-1);
		/* link should still be valid, but better check again */
		if (editedname)
		{
			if (misc_check_if_ed2klink_is_valid(lh_ed2klink_utf8, &name, &size, &hashstr,NULL,NULL))
			{
				if ((name)&&(hashstr)&&(size>0))
				{
					gchar *link = g_strdup_printf("ed2k://|file|%s|%u|%s|/", editedname, size, hashstr);
					if (link)
					{
						if(!linkhandler_append_link_to_pending_file(link))
						{
							errmsg = _(" could not write to file ~/.ed2k_gui/pending.txt to remember link. ");
						}
						g_free(link);
					} else errmsg = "g_strdup_printf() failed ?!";
				} else errmsg = "link ok, but fields bad ?!";
			} else errmsg = "link turned sour ?!";
		} else errmsg = "editedname==NULL ?!";
	} else errmsg = "linkhandler==NULL ?!";

	G_FREE(name);
	G_FREE(hashstr);
	G_FREE(editedname);

	if (!errmsg)
	{
		/* check whether the chosen action should become our default action */
		if (lh_cb_default_action)
		{
			gboolean checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(lh_cb_default_action));

			if ((checked) && action != opt_get_int(OPT_GUI_LINKHANDLER_ACTION_IF_NO_GUI_RUNNING))
			{
				opt_set_int(OPT_GUI_LINKHANDLER_ACTION_IF_NO_GUI_RUNNING, action);
				opt_write_to_disk();
			}
		}
		
		if (action == LINKHANDLER_IF_NO_GUI_START_GUI)
		{
			lh_main_run_gui_override_flag = TRUE;
		}
		gtk_widget_destroy(lh_window);
		return;
	}

	g_return_if_fail (lh_statuslabel!=NULL);
	gtk_label_set_text(GTK_LABEL(lh_statuslabel), UTF8_SHORT_PRINTF("%s", errmsg));
	gtk_widget_show(lh_statuslabel);
	gtk_widget_grab_focus (lh_entry);
}


/******************************************************************************
 *
 *   linkhandler_create_no_gui_running_buttons
 *
 *   creates a button box that asks the user what he/she 
 *    wants to do if no running GUI has been found.
 *
 */
 
static GtkWidget *
linkhandler_create_no_gui_running_buttons(void)
{
	GtkWidget	*box = NULL;
	GtkWidget	*vbox = NULL;
	GtkWidget *button_cancel = NULL;
	GtkWidget *button_start_gui = NULL;
	GtkWidget *button_remember_link = NULL;

	vbox = gtk_vbox_new(FALSE, 5);	
	g_return_val_if_fail (vbox!=NULL, NULL);
	gtk_widget_show (vbox);
	
	box = gtk_hbutton_box_new();
	g_return_val_if_fail (box!=NULL, NULL);
	gtk_widget_show (box);

	new_button_pack_and_signal_connect ( &button_start_gui,
	                                     box,
	                                     _(" start GUI now "),
	                                     linkhandler_ask_buttons_callback,
	                                     GUINT_TO_POINTER(LINKHANDLER_IF_NO_GUI_START_GUI),
	                                     1);

	new_button_pack_and_signal_connect ( &button_remember_link,
	                                     box,
	                                     _(" remember link for later "),
	                                     linkhandler_ask_buttons_callback,
	                                     GUINT_TO_POINTER(LINKHANDLER_IF_NO_GUI_REMEMBER_AND_QUIT),
	                                     1);

	new_button_pack_and_signal_connect ( &button_cancel,
	                                     box,
	                                     _(" cancel "),
	                                     linkhandler_destroy,
	                                     NULL,
	                                     1);
	gtk_box_pack_start(GTK_BOX(vbox), box, FALSE, FALSE, 15);


	lh_cb_default_action = gtk_check_button_new_with_label ( UTF8_SHORT_PRINTF(_(" make this the default action and do not ask in the future. ")) );

	gtk_box_pack_start (GTK_BOX(vbox), lh_cb_default_action, FALSE, FALSE, 2);

	gtk_widget_show (lh_cb_default_action);

	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(lh_cb_default_action), FALSE);
	
	return vbox;
}


/******************************************************************************
 *
 *   linkhandler_create_buttons
 *
 *   creates a horizontal packing box with buttons that give you
 *   a choice which GUI instance to send the link to.
 *
 */


static GtkWidget *
linkhandler_create_buttons (void)
{
	GtkWidget	*hbox = NULL;
	GtkWidget *button;

	hbox = gtk_hbutton_box_new();
	g_return_val_if_fail (hbox!=NULL, NULL);
	gtk_widget_show (hbox);

	if (g_slist_length(lh_pipes_found) == 1)
	{
		GtkWidget *ok_button;

		ok_button = gtk_button_new_from_stock (GTK_STOCK_OK);

		g_signal_connect (ok_button, "clicked",
		                  G_CALLBACK (linkhandler_instance_button_callback),
		                  g_strdup ((gchar*)lh_pipes_found->data));


		gtk_box_pack_start (GTK_BOX (hbox), ok_button, FALSE, TRUE, 2);
		gtk_widget_show (ok_button);
	}
	else
	{
		GSList *node;

		for ( node = lh_pipes_found; node != NULL; node = node->next )
		{
			gchar     *basename, *postfix, buttontxt[64];

			if (!node->data)
				continue;

			basename = g_path_get_basename((gchar*)node->data);
			postfix = basename + 5;  /* 5 = strlen("pipe.") */

			if (!basename)
				continue;

			if (*(postfix-1)!=0x00)
				g_snprintf(buttontxt,sizeof(buttontxt), _(" GUI #%s "), postfix);
			else
				g_snprintf(buttontxt,sizeof(buttontxt), " %s ", _(" main GUI "));

			new_button_pack_and_signal_connect ( &button, hbox, buttontxt, linkhandler_instance_button_callback,
			                                     g_strdup( (gchar*) node->data ), 1 );

			g_free(basename);
		}
	}

	return hbox;
}


/******************************************************************************
 *
 *   linkhandler_delete_event
 *
 ******************************************************************************/

static gint
linkhandler_delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
	return FALSE;	/* window can be destroyed */
}


/******************************************************************************
 *
 *   linkhandler_destroy
 *
 ******************************************************************************/

static void
linkhandler_destroy (GtkWidget *widget, gpointer data)
{
	gtk_main_quit();
}


/******************************************************************************
 *
 *   linkhandler_create_options_chooser
 *
 ******************************************************************************/

static GtkWidget *
linkhandler_create_options_chooser (void)
{
	const gchar *labeltext;
	GtkWidget   *cb_vbox, *vbox, *label, *align;
	gboolean     is_active;
	
	vbox = gtk_vbox_new (FALSE, 2);
	
	cb_vbox = gtk_vbox_new (FALSE, 2);

	align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
	gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 6, 12, 0);
	gtk_container_add (GTK_CONTAINER (align), cb_vbox);
	
	label = gtk_label_new (NULL);
	labeltext = UTF8_SHORT_PRINTF ("<b>%s</b>", _("Options"));
	gtk_label_set_markup (GTK_LABEL (label), labeltext);
	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
	
	labeltext = UTF8_SHORT_PRINTF ("%s", _("Pause download"));
	lh_cb_dlnow = gtk_check_button_new_with_label (labeltext);

	is_active = opt_get_bool (OPT_GUI_LINKHANDLER_ADD_PAUSED_DEFAULT);
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (lh_cb_dlnow), is_active);
	gtk_box_pack_start (GTK_BOX (cb_vbox), lh_cb_dlnow, FALSE, FALSE, 0);

	gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);

	gtk_widget_show (lh_cb_dlnow);
	gtk_widget_show (vbox);
	gtk_widget_show (cb_vbox);
	gtk_widget_show (label);
	gtk_widget_show (align);
	gtk_widget_show (vbox);
	
	return vbox;
}

/******************************************************************************
 *
 *   linkhandler_priority_radiobutton_cb
 *
 ******************************************************************************/

static void
linkhandler_priority_radiobutton_cb (GtkRadioButton *rb, const char *prio_suffix)
{
	g_return_if_fail (prio_suffix != NULL);
	
	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rb)))
		lh_prio_suffix = prio_suffix;
}

/******************************************************************************
 *
 *   linkhandler_create_priority_chooser
 *
 ******************************************************************************/

static GtkWidget *
linkhandler_create_priority_chooser (void)
{
	const gchar *labeltext;
	GtkWidget   *l, *vbox, *rb_vbox, *align;
	GtkWidget   *rb_low, *rb_normal, *rb_high;
	GSList      *g;
	
	rb_vbox = gtk_vbox_new (FALSE, 0);
	
	align = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
	gtk_alignment_set_padding (GTK_ALIGNMENT (align), 0, 6, 12, 0);
	gtk_container_add (GTK_CONTAINER (align), rb_vbox);

	vbox = gtk_vbox_new (FALSE, 2);
	
	l = gtk_label_new (NULL);
	labeltext = UTF8_SHORT_PRINTF ("<b>%s</b>", _("Priority"));
	gtk_label_set_markup (GTK_LABEL (l), labeltext);
	gtk_misc_set_alignment (GTK_MISC (l), 0.0, 0.5);
	gtk_box_pack_start (GTK_BOX (vbox), l, FALSE, FALSE, 0);
	gtk_box_pack_start (GTK_BOX (vbox), align, TRUE, TRUE, 0);

	g = NULL;
	rb_low = gtk_radio_button_new_with_label (g, _("Low"));
	gtk_box_pack_start (GTK_BOX (rb_vbox), rb_low, TRUE, TRUE, 0);
	
	g = gtk_radio_button_get_group (GTK_RADIO_BUTTON (rb_low));
	rb_normal = gtk_radio_button_new_with_label (g, _("Normal"));
	gtk_box_pack_start (GTK_BOX (rb_vbox), rb_normal, TRUE, TRUE, 0);
	
	g = gtk_radio_button_get_group (GTK_RADIO_BUTTON (rb_normal));
	rb_high = gtk_radio_button_new_with_label (g, _("High"));
	gtk_box_pack_start (GTK_BOX (rb_vbox), rb_high, TRUE, TRUE, 0);
	
	g_signal_connect (rb_low, "toggled", 
	                  G_CALLBACK (linkhandler_priority_radiobutton_cb),
	                  "|low|");
	
	g_signal_connect (rb_normal, "toggled", 
	                  G_CALLBACK (linkhandler_priority_radiobutton_cb),
	                  "|normal|");
	
	g_signal_connect (rb_high, "toggled", 
	                  G_CALLBACK (linkhandler_priority_radiobutton_cb),
	                  "|high|");
	
	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rb_normal), TRUE);

	gtk_widget_show (l);
	gtk_widget_show (align);
	gtk_widget_show (rb_low);
	gtk_widget_show (rb_normal);
	gtk_widget_show (rb_high);
	gtk_widget_show (rb_vbox);
	gtk_widget_show (vbox);
	
	return vbox;
}

/******************************************************************************
 *
 *   linkhandler_create_mainwindow
 *
 ******************************************************************************/

#define LHWIN_SPACING	12

static void
linkhandler_create_mainwindow (const gchar *ed2klink_utf8)
{
	GtkWidget	*vbox;
	GtkWidget	*label;
	GtkWidget	*ruler;
	GtkWidget	*hbox;
	gchar    	*labeltxt;
	gchar    	*name = NULL;
	gchar    	*hashstr = NULL;
	guint    	 size = 0;

	g_return_if_fail ( ed2klink_utf8 != NULL );

	lh_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_widget_show (lh_window);
	gtk_window_set_title (GTK_WINDOW(lh_window), _("eDonkey2000 - link"));
	gtk_window_set_position (GTK_WINDOW(lh_window), GTK_WIN_POS_CENTER_ALWAYS);
	gtk_container_set_border_width (GTK_CONTAINER(lh_window), LHWIN_SPACING);
	gtk_window_set_default_size (GTK_WINDOW(lh_window), 320, 240);

	g_signal_connect (G_OBJECT (lh_window), "destroy",
	                  G_CALLBACK(linkhandler_destroy), NULL);
	g_signal_connect (G_OBJECT (lh_window), "delete_event",
	                  G_CALLBACK(linkhandler_delete_event), NULL);

	vbox = gtk_vbox_new(FALSE,0);
	g_return_if_fail (vbox!=NULL);
	gtk_widget_show (vbox);
	gtk_container_add (GTK_CONTAINER(lh_window), vbox);
	gtk_container_set_border_width (GTK_CONTAINER(vbox), LHWIN_SPACING);

	if (lh_error_msg == NULL)
	{
		if (!misc_check_if_ed2klink_is_valid (ed2klink_utf8, &name, &size, &hashstr, NULL, NULL))
		{
			labeltxt = g_strdup_printf ("%s:\n\n %s ", UTF8_SHORT_PRINTF("%s",_("received invalid ed2k-link")), ed2klink_utf8);
		}
		else
		{
			GString *s;
			
			s = g_string_new (" ");
			g_string_append (s, name);
			g_string_append (s, " \n\n ");
			g_string_append (s, misc_get_human_size_utf8 (NULL, 0, size));
			g_string_append (s, " ");
			labeltxt = g_string_free (s, FALSE);
		}
	}
	else
	{
		labeltxt = TO_UTF8(lh_error_msg);
	}

	g_return_if_fail (labeltxt!=NULL);

	label = gtk_label_new(labeltxt);
	g_return_if_fail (label!=NULL);
	gtk_label_set_justify(GTK_LABEL(label),GTK_JUSTIFY_LEFT);
	gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
	gtk_widget_show (label);
	gtk_container_set_border_width (GTK_CONTAINER(lh_window), 5);
	gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, LHWIN_SPACING);

	/* valid ed2k-link => show entry widget and buttons box, otherwise only CANCEL button */
	if ((name) && (hashstr) && size>0)
	{
		GtkWidget *pausebox, *priobox;

		lh_entry = gtk_entry_new();
		g_return_if_fail (lh_entry!=NULL);
		gtk_widget_show(lh_entry);
		gtk_box_pack_start(GTK_BOX(vbox), lh_entry, TRUE, TRUE, LHWIN_SPACING);
		gtk_entry_set_text(GTK_ENTRY(lh_entry), name);	/* name will be UTF-8 */

		gtk_widget_grab_focus (lh_entry);

		pausebox = linkhandler_create_options_chooser ();
		gtk_box_pack_start(GTK_BOX(vbox), pausebox, FALSE, FALSE, LHWIN_SPACING/2);
		
		priobox = linkhandler_create_priority_chooser ();
		gtk_box_pack_start(GTK_BOX(vbox), priobox, FALSE, FALSE, LHWIN_SPACING/2);
		
		ruler = gtk_hseparator_new();
		g_return_if_fail (ruler!=NULL);
		gtk_widget_show (ruler);
		gtk_box_pack_start(GTK_BOX(vbox), ruler, FALSE, FALSE, LHWIN_SPACING/2);

		if (lh_pipes_found != NULL)
		{
			hbox = linkhandler_create_buttons();
		}
		else
		{
			hbox = linkhandler_create_no_gui_running_buttons();
		}

		g_return_if_fail (hbox!=NULL);
		gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, LHWIN_SPACING/2);
	}
	else
	{
		GtkWidget *cancelbutton = gtk_button_new_with_label(UTF8_SHORT_PRINTF("%s",_(" OK. Sorry, won't happen again. ")));
		gtk_widget_show(cancelbutton);
		(void) g_signal_connect (G_OBJECT (cancelbutton),
		                         "clicked",
		                         G_CALLBACK(linkhandler_destroy),
		                         NULL);
		gtk_box_pack_start(GTK_BOX(vbox), cancelbutton, FALSE, FALSE, LHWIN_SPACING/2);
	}

	lh_statuslabel = gtk_label_new(" ");
	gtk_box_pack_start(GTK_BOX(vbox), lh_statuslabel, TRUE, TRUE, LHWIN_SPACING/2);
	gtk_widget_hide (lh_statuslabel);

	g_free(labeltxt);
}


/******************************************************************************
 *
 *   linkhandler_append_link_to_pending_file
 *
 *   appends an ed2k-link to the file ~/.ed2k_gui/pending_links.txt
 *     no main GUI window is needed
 *
 *   returns TRUE if everything went fine, and FALSE on error
 *
 *   note: links passed should be in UTF-8
 *
 ***/

 static gboolean 
 linkhandler_append_link_to_pending_file (const gchar *link)
 {
 	const gchar *fn;
	FILE        *fout;
	
	g_return_val_if_fail (link != NULL, FALSE);

	fn = opt_get_opt_filename_without_instance("pending_links.txt");

	g_return_val_if_fail (fn != NULL, FALSE);
	
	fout = fopen (fn, "a");
	if (!fout)
		return FALSE;
	
	fprintf (fout, "%s\n", link);
	fclose(fout);
	
	return TRUE;
 }

 
/******************************************************************************
 *
 *   linkhandler_do_check_for_pending_links
 *
 ***/
 
static void
linkhandler_do_check_for_pending_links (void)
{
	const gchar *pendingfn = opt_get_opt_filename_without_instance("pending_links.txt");

	if (pendingfn)
	{
#ifdef G_OS_WIN32	
		_unlink(pendingfn);
#else
		unlink(pendingfn);
#endif
	}

	
	if (lh_pending_links_array)
	{
		gchar **line = lh_pending_links_array;
		
		while (*line)
		{
			if (**line)
			{
				gchar   *fn = NULL;
				gchar   *hashstr = NULL;
				guint32  size = 0;
				gboolean addpaused = FALSE;
				guint    addpriority = 1; /* normal */

				/* links should be saved in UTF-8 encoding */
				if (misc_check_if_ed2klink_is_valid(*line,&fn,&size,&hashstr,&addpaused,&addpriority))
				{
					if ((fn)&&(hashstr)&&(size>0))
					{
						guint8 hash[16];
						memcpy(hash,hash_str_to_hash(hashstr),16);
						status_message_blue (_("GUI: adding pending link '%s' to downloads.\n"), *line);
						gui_core_conn_send_new_download(core, hash, size, fn); /* download immediately! */

						if (addpaused)
						{
							gui_core_conn_send_pause_download(core, hash); /* pause download */
							/* note: do not toggle default option for this stuff here */
						}
						
						if (addpriority != 1)
						{
							gui_core_conn_send_set_download_priority (core, hash, (guint8) addpriority);
							/* note: do not toggle default option for this stuff here */
						}
					} else status_warning (_("GUI: ignored pending link '%s' (not valid)\n"), *line);
				} else status_warning (_("GUI: ignored pending link '%s' (not valid)\n"), *line);
			}
			line++;
		}
	}
}


/******************************************************************************
 *
 *   linkhandler_check_for_pending_links_callback
 *
 *   TODO: do we still need this to be an idle callback?
 *
 ******************************************************************************/

static gboolean
linkhandler_check_for_pending_links_callback (GtkWidget *dialog)
{
	gint ret;

	ret = gtk_dialog_run (GTK_DIALOG(dialog));

	gtk_widget_destroy(dialog);

	if ( ret != GTK_RESPONSE_YES )
	{
		status_message_blue (_("If you want to make the GUI forget the remembered links, you need to delete the file ~/.ed2k_gui/pending_links.txt"));
	}
	else
	{
		linkhandler_do_check_for_pending_links();
	}

	if (lh_pending_links_array)
	{
		g_strfreev(lh_pending_links_array);
		lh_pending_links_array = NULL;
	}

	return FALSE; /* don't call us again */
}

 /******************************************************************************
 *
 *   linkhandler_check_for_pending_links
 *
 *   checks whether the file ~/.ed2k_gui/pending_links.txt
 *     exists, and if yes pops up a dialog asking whether
 *     those pending downloads should be added now.
 *
 */

void
linkhandler_check_for_pending_links (void)
{
	GtkWidget   *dialog;
	const gchar *fn, *msg;
	gchar       **lines, **line;
	guint        num = 0;

	fn = opt_get_opt_filename("pending_links.txt");
	g_return_if_fail (fn != NULL);

	lines = misc_get_array_of_lines_from_textfile(fn, FALSE, FALSE);

	if (!lines)
		return;

	line = lines;
	while (*line)
	{
		/* links should have been saved in UTF-8 encoding */
		if (misc_check_if_ed2klink_is_valid(*line, NULL, NULL, NULL, NULL, NULL))
		{
			num++;
		}
		line++;
	}

	if (num == 0)
	{
		status_message_blue (_("GUI: Removing file %s with pending ed2k-links. It contains no valid pending ed2k-links.\n"), fn);
		
#ifdef G_OS_WIN32
		_unlink(fn);
#else
		unlink(fn);
#endif

		g_strfreev(lines);
		return;
	}

	msg = UTF8_SHORT_PRINTF (_("There are %u pending ed2k-links.\n\nDo you want to add them to the download list now?"), num);

	dialog = gtk_message_dialog_new ( GTK_WINDOW(window),
	                                  GTK_DIALOG_DESTROY_WITH_PARENT,
	                                  GTK_MESSAGE_QUESTION,
	                                  GTK_BUTTONS_YES_NO,
	                                  msg );

	lh_pending_links_array = lines;

	g_idle_add ( (GSourceFunc) linkhandler_check_for_pending_links_callback, dialog);
}


/******************************************************************************
 *
 *   linkhandler_send_link_to_all_pipes
 *
 *   sends the given link to all found pipes and
 *     frees the list of found pipes.
 *
 ******************************************************************************/

static void
linkhandler_send_link_to_all_pipes (const gchar *link)
{
	GSList *node = lh_pipes_found;
	gchar  *xlink;

	g_return_if_fail (link != NULL);

	xlink = g_strconcat(link, "\n\n", NULL);

	while (node)
	{
		if (node->data)
		{
			linkhandler_send_link_to_pipe((gchar*)node->data, xlink);
			g_free(node->data);
			node->data = NULL;
		}
		node = node->next;
	}

	g_free(xlink);
	g_slist_free(lh_pipes_found);
	lh_pipes_found = NULL;
}

/******************************************************************************
 *
 *   linkhandler_main
 *
 *   returns TRUE if it dealt with the link and
 *     no main GUI window is needed
 *
 *   returns FALSE if a main GUI window is needed
 *
 */

gboolean
linkhandler_main (const gchar *ed2klink)
{
	gchar *ed2klink_ascii;

	if (ed2klink == NULL)
		return FALSE;

	ed2klink_ascii = g_strdup(ed2klink);
	misc_strings_unescape_url (ed2klink_ascii);
	lh_ed2klink_utf8 = TO_UTF8(ed2klink_ascii);
	G_FREE(ed2klink_ascii);

#if defined(G_OS_UNIX) || defined(G_OS_WIN32)
	if (linkhandler_check_for_existing_GUI_instances() == -1)
		lh_error_msg = "check for existing GUI instances FAILED (see console output).";
#endif

	if (lh_error_msg == NULL)
	{
		/* server link? behave differently, these are not so important */
		if (g_ascii_strncasecmp(lh_ed2klink_utf8, "ed2k:", 5) == 0
			&& strstr(lh_ed2klink_utf8, "|server|") != NULL)
		{
			if (lh_pipes_found == NULL)
			{
				lh_error_msg = " Received ed2k server link, but did not find any running GUIs. ";
			}
			else
			{
				linkhandler_send_link_to_all_pipes(lh_ed2klink_utf8);
				G_FREE(lh_ed2klink_utf8);
				return TRUE; /* done, no main GUI window needed */
			}
		}
		else
		{
			if (lh_pipes_found == NULL)
			{
				switch (opt_get_int(OPT_GUI_LINKHANDLER_ACTION_IF_NO_GUI_RUNNING))
				{
					case LINKHANDLER_IF_NO_GUI_ASK:
					break;

					case LINKHANDLER_IF_NO_GUI_START_GUI:
					{
						G_FREE(lh_ed2klink_utf8);
						return FALSE;		/* return now and run main GUI */
					}

					case LINKHANDLER_IF_NO_GUI_REMEMBER_AND_QUIT:
					{
						if(linkhandler_append_link_to_pending_file(lh_ed2klink_utf8))
						{
							G_FREE(lh_ed2klink_utf8);
							return TRUE;	/* quit now, no main GUI window */
						}
						lh_error_msg = _(" could not write to file ~/.ed2k_gui/pending.txt to remember link. ");
					}
					break;

					default:
					{
						lh_error_msg = "FIXME: unknown OPT_GUI_LINKHANDLER_ACTION_IF_NO_GUI_RUNNING value.";
					}
				}
			}
		}
	}

	linkhandler_create_mainwindow(lh_ed2klink_utf8);

	gtk_main ();

	G_FREE(lh_ed2klink_utf8);

	if (lh_main_run_gui_override_flag)
	{
		return FALSE;	/* we remembered the link, but still want the GUI to start NOW */
	}

	return TRUE; /* quit now, no main GUI window */
}




