/*
 * Copyright (C) 2016-2021 m-privacy GmbH
 *
 * This 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 software 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 software; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#include <unistd.h>

#include <cerrno>

#include <rfb/LogWriter.h>

#include <FL/Fl.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Box.H>
#include <FL/fl_ask.H>

#include <FL/Fl_Button.H>
#include <FL/Fl_Return_Button.H>
#include <FL/Fl_Check_Button.H>
#include <FL/Fl_Text_Display.H>
#include <FL/Fl_Multiline_Output.H>
#include <FL/Fl_Window.H>

#if defined(WIN32) || defined(WIN64)
#include "win_registry_utils.h"
#include <winsock2.h>
#include <windows.h>
#else
#include <dirent.h>
#include <regex>
#include <sys/socket.h>
#include <sys/un.h>
#endif /*defined(WIN32) || defined(WIN64)*/

#include "MagicUrlHelper.h"
#include "tgpro_environment.h"
#include "mp_utils.h"
#include "i18n.h"
#include "parameters.h"
#include "dpi_scale.h"

static rfb::LogWriter vlog("MagicUrlHelper");

static int askAgainNextTime = 1;
static int userWantsToSetStandardBrowser = 1;
static const char* magicUrlPath = NULL;
static int magicUrlInstalled = 0;
static char *userpath = getenv("HOME");
#if ! (defined(WIN32) || defined(WIN64) )
static char *kde_path = NULL;
static char *kde_path2 = NULL;
static char *mimeapps_path = NULL;
static char buffer[256];
#endif

MagicUrlHelper::MagicUrlHelper() : standard_browser_question(NULL) {
#if defined(WIN32) || defined(WIN64)
	magicUrlPath = get_browserchoice_exe_path();
#else
		char* magic_inst_path;
		magic_inst_path = (char *) malloc (strlen(MAGICURL_INSTALL_PATH) + 1);
		strcpy(magic_inst_path, MAGICURL_INSTALL_PATH);
		magic_inst_path[strlen(MAGICURL_INSTALL_PATH)] = '\0';

		char* magicurldesktop;
		magicurldesktop = (char *) malloc(strlen(MAGICURL_DESKTOP) + 1);
		strcpy(magicurldesktop, MAGICURL_DESKTOP);
		magicurldesktop[strlen(MAGICURL_DESKTOP)] = '\0';

		if (file_exists(magic_inst_path) && file_exists(magicurldesktop)) {
				 magicUrlPath = magic_inst_path;
		}
		free(magic_inst_path);
		free(magicurldesktop);

		kde_path = (char *) malloc(strlen(userpath) + strlen(KDEGLOBALS) + 1);
		strcpy(kde_path, userpath);
		strcat(kde_path, KDEGLOBALS);
		kde_path[strlen(userpath) + strlen(KDEGLOBALS)] = '\0';

		/* Under KDE Plasma the default browser needs to be defined under another path. */
		kde_path2 = (char *) malloc(strlen(userpath) + strlen(KDEGLOBALS2) + 1);
		strcpy(kde_path2, userpath);
		strcat(kde_path2, KDEGLOBALS2);
		kde_path2[strlen(userpath) + strlen(KDEGLOBALS2)] = '\0';

		mimeapps_path = (char *) malloc(strlen(userpath) + strlen(MIMEAPPS) + 1);
		strcpy(mimeapps_path, userpath);
		strcat(mimeapps_path, MIMEAPPS);
		mimeapps_path[strlen(userpath) + strlen(MIMEAPPS)] = '\0';
#endif
	if (magicUrlPath) {
		magicUrlInstalled = 1;
				vlog.debug("Magicurl is installed.");
	}
}

MagicUrlHelper::~MagicUrlHelper() {
	if (standard_browser_question)
		free(standard_browser_question);
}

/*
 * Returns 1 if TightGate-Pro was set as standard browser. Actually, if the Windows dialog was launched.
 * Returns 0 in all other cases.
 *
 */
bool MagicUrlHelper::maybeSetStandardBrowser() {
	bool standardBrowserSet = false;
#if defined(WIN32) || defined(WIN64)
	if (!isMagicUrlInstalled()) { // Only check for XFCE if not Windows
		vlog.info("MagicUrl is not installed. Cannot set (or ask the user if it should be set) vncviewer as standard for opening web pages");
#else
	if (isMagicUrlInstalled() == 0 || (strcmp(getenv("XDG_CURRENT_DESKTOP"),"XFCE")) == 0) {
		vlog.info("MagicUrl is not installed (or Desktop Environment is XFCE). Cannot set (or ask the user if it should be set) vncviewer as standard for opening web pages");
#endif
	} else {
		int magicUrlHasMoved = hasMoved();
		if (magicUrlHasMoved) {
			vlog.error("It seems like Browserchoice.exe is not where it is supposed to be. Did you install a newer version? New versions move the program files from \"C:\\Program Files (x86)\" to \"C:\\Program Files\". In this case, you should only see this message once. If you see this message every time you start the Viewer, it might be a GPO (Group Policy Objects) misconfiguration. Removing registry keys...");
			resetStandardBrowser(); // The values in the registry aren't valid anymore. Better just remove them. This also cleans up if MagicUrl was uninstalled and the user launched the viewer.
		}
		int alreadyStandardBrowser = isStandardBrowser();
		if (!alreadyStandardBrowser || magicUrlHasMoved) {
			if (standardBrowserEnforce) {
				vlog.info("Enforce standard browser\n");
				setStandardBrowser();
				standardBrowserSet = true;
			} else {
				const char* test = standardBrowserPrompt ? "yes" : "no";
				vlog.debug("Saved 'ask again to set standard browser': %s\n", test);

				if (standardBrowserPrompt || magicUrlHasMoved) { // hasMoved: ask anyway if the Browserchoice.exe found in the registry doesn't exist anymore
					promptStandardBrowser();
					standardBrowserPrompt.setParam((bool) isAskAgainNextTime());
					if (isUserWantsToSetStandardBrowser()) {
						setStandardBrowser();
						standardBrowserSet = true;
					}
				}
			}
		}
	}

	return standardBrowserSet;
}

static void yes_no_button_callback(Fl_Widget* widget, void* val) {
	userWantsToSetStandardBrowser = (fl_intptr_t) val;
	const char* userChoice = userWantsToSetStandardBrowser ? "yes" : "no";
	vlog.info("Set as standard browser? User clicked '%s'", userChoice);
	const char* askAgainNextTimeAsString = askAgainNextTime ? "yes" : "no";
	vlog.info("Ask again next time: %s", askAgainNextTimeAsString);
	widget->window()->hide();
}

static void checkbox_callback(Fl_Widget* widget, void* val) {
	Fl_Check_Button* check_button = (Fl_Check_Button* ) widget;
	askAgainNextTime = !check_button->value();
}

int MagicUrlHelper::promptStandardBrowser() {
	const char* standard_browser_title = _("Default Browser");
	size_t question_length = scale(300 + 2*strlen(productName.getData()));
#ifdef WIN32
	POINT cursorPos;
#endif

	if (standard_browser_question)
		free(standard_browser_question);
	standard_browser_question = (char *) malloc(question_length);
	if (!standard_browser_question)
		return -1;
	snprintf(standard_browser_question, question_length - 1, _("%s isn't the default browser. Do you want to make %s the default browser?"), productName.getData(), productName.getData());

	int win_width = scale(580);
	int win_height = scale(145);

	Fl_Window* win = new Fl_Window(win_width, win_height, standard_browser_title);
#ifdef WIN32
	if(GetCursorPos(&cursorPos))
		win->position(cursorPos.x, cursorPos.y);
#else
	win->position(Fl::w() / 2 - win->w() / 2,
			  Fl::h() / 2 - win->h() / 2);
#endif

	Fl_Box* box = new Fl_Box(scale(30), scale(10), scale(560), scale(40), standard_browser_question);
	box->align(FL_ALIGN_INSIDE | FL_ALIGN_WRAP | FL_ALIGN_LEFT);

	Fl_Check_Button* check_button = new Fl_Check_Button(scale(30), scale(60), scale(180), scale(25), _("Don't ask again"));
	check_button->callback(checkbox_callback, (void*) 0);

	Fl_Button *button;

	button = new Fl_Button(win->w() - scale(100), scale(110), scale(90), scale(25), fl_no);
	button->align(FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
	button->callback(yes_no_button_callback, (void*) 0);
	button->shortcut(FL_Escape);

	button = new Fl_Return_Button(win->w() - scale(200), scale(110), scale(90), scale(25), fl_yes);
	button->align(FL_ALIGN_INSIDE|FL_ALIGN_WRAP);
	button->callback(yes_no_button_callback, (void*) 1);

	scaleWidgetFonts(win);

	win->end();
	win->show();
	return Fl::run(); // If I don't run this, the rest of the program continues to load, and although this might be nice, the program is not yet ready to handle the feedback asynchronously
}


int MagicUrlHelper::isStandardBrowser() {
	int isStandardBrowser = 0;
#if defined(WIN32) || defined(WIN64)
	char* registryValue = getRegistryKeyValue(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice", "ProgId");

	if (registryValue) {
		isStandardBrowser = strstr_insensitive(registryValue, "MP.HTTP") ? 1 : 0;
		free(registryValue);
	}
#else
/* Under KDE all other settings, except for the kdeglobals, seem not to help. So this is the essential file to look for. */
	if (file_exists(kde_path) && file_exists(kde_path2) && (strcmp(getenv("XDG_CURRENT_DESKTOP"),"KDE")) == 0) {
		sprintf(buffer, "grep -q BrowserApplication\\\\[\\$e\\\\]=magicurl.desktop %s", kde_path);
		if (system(buffer) == 0) {
			sprintf(buffer, "grep -q BrowserApplication\\\\[\\$e\\\\]=magicurl.desktop %s", kde_path2);
			if (system(buffer) == 0) {
				isStandardBrowser = 1;
			} else {
				isStandardBrowser = 0;
			}
		} else {
			isStandardBrowser = 0;
		}
/* On all other desktop environments it seems to be enough if magicurl is listet in the mimeapps file. */
	} else if (file_exists(mimeapps_path)) {
		sprintf(buffer, "grep -q x-scheme-handler/http=magicurl.desktop %s", mimeapps_path);
		if (system(buffer) == 0) {
			isStandardBrowser = 1;
		} else {
			isStandardBrowser = 0;
		}
	} else {
		isStandardBrowser = 0;
	}
#endif

	const char* isStandardBrowserAsString = isStandardBrowser ? "yes" : "no";
	vlog.info("Is MagicUrl already set as standard browser? %s", isStandardBrowserAsString);

	return isStandardBrowser;
}

int MagicUrlHelper::hasMoved() {
	int hasMoved = 0;
#if defined(WIN32) || defined(WIN64)
	char* browserchoiceExePath = getRegistryKeyValue(HKEY_CURRENT_USER, "Software\\Classes\\MP.HTTPS\\shell\\open\\command", NULL);
	if (browserchoiceExePath) {
		char* params = strstr(browserchoiceExePath, " -url");
		if (params) {
			params[0] = '\0';
		}
		char* browserchoiceExePathTmp = browserchoiceExePath;
		if (browserchoiceExePathTmp[0] == '\"') {
			browserchoiceExePathTmp++;
		}
		if (browserchoiceExePathTmp[strlen(browserchoiceExePathTmp) - 1] == '\"') {
			browserchoiceExePathTmp[strlen(browserchoiceExePathTmp) - 1] = '\0';
		}
		hasMoved = !file_exists(browserchoiceExePathTmp);
		if (hasMoved) {
			vlog.error("There is no Browserchoice.exe where the registry says it should be (HKEY_CURRENT_USER\\Software\\Classes\\MP.HTTPS\\shell\\open\\command): %s", browserchoiceExePathTmp);
		} else {
			vlog.debug("There is a Browserchoice.exe where the registry says it should be (HKEY_CURRENT_USER\\Software\\Classes\\MP.HTTPS\\shell\\open\\command): %s", browserchoiceExePathTmp);
		}
		free(browserchoiceExePath);
	} else {
		vlog.info("Failed to find MagicUrl location in the registry (HKEY_CURRENT_USER\\Software\\Classes\\MP.HTTPS\\shell\\open\\command). This probably means it was never set as standard browser. Assuming it hasn't moved (to be more precise, that it's irrelevant, as there is no key that could be wrong).");
		hasMoved = 0;
	}
#endif
	return hasMoved;
}

int MagicUrlHelper::setStandardBrowser() {
	int result = 0;
#if defined(WIN32) || defined(WIN64)
	if (configDir.getData() && strlen(configDir.getData())) {
		const char* format_string = "\"%s\" -setregistry -configdir \"%s\"";
		const size_t bc_call_len = strlen(magicUrlPath) + strlen(format_string) - 2*strlen("%s") + strlen(magicUrlPath) + strlen(configDir.getData()) + 1;
		char bc_call[bc_call_len];
		snprintf(bc_call, bc_call_len, format_string, magicUrlPath, configDir.getData());
		result = launch_program(bc_call, 0, 0, 0, NULL);
		vlog.info("Setting standard browser using: %s", bc_call);
	} else {
		const char* format_string = "\"%s\" -setregistry";
		const size_t bc_call_len = strlen(magicUrlPath) + strlen(format_string) - strlen("%s") + strlen(magicUrlPath) + 1;
		char bc_call[bc_call_len];
		snprintf(bc_call, bc_call_len, format_string, magicUrlPath);
		result = launch_program(bc_call, 0, 0, 0, NULL);
		vlog.info("Setting standard browser using: %s", bc_call);
	}

#else
		if (strcmp(getenv("XDG_CURRENT_DESKTOP"), "KDE") == 0) {
				updateKdeglobals(kde_path);
				// For KDE Plasma.
				updateKdeglobals(kde_path2);
		// For any desktop environment but KDE
		} else {
				// If mimeapps.list exists and add/substitute lines for http, https and about.
				if (file_exists(mimeapps_path)) {
						sprintf(buffer, "grep -q \\\\[Default Applications\\\\] %s", mimeapps_path);
						if (system(buffer) != 0) {
								sprintf(buffer, "echo [Default Applications] >> %s", mimeapps_path);
								system(buffer);
						}
						sprintf(buffer, "grep -q x-scheme-handler/http= %s", mimeapps_path);
						if (system(buffer) == 0) {
								sprintf(buffer, "sed -i 's/x-scheme-handler\\/http=.*/x-scheme-handler\\/http=magicurl.desktop/g' %s", mimeapps_path);
								system(buffer);
						} else {
								sprintf(buffer, "sed -i 's/\\[Default Applications\\]/\\[Default Applications\\]\\nx-scheme-handler\\/http=magicurl.desktop/g' %s", mimeapps_path);
								system(buffer);
						}
						sprintf(buffer, "grep -q x-scheme-handler/https= %s", mimeapps_path);
						if (system(buffer) == 0) {
								sprintf(buffer, "sed -i 's/x-scheme-handler\\/https=.*/x-scheme-handler\\/https=magicurl.desktop/g' %s", mimeapps_path);
								system(buffer);
						} else {
								sprintf(buffer, "sed -i 's/\\[Default Applications\\]/\\[Default Applications\\]\\nx-scheme-handler\\/https=magicurl.desktop/g' %s", mimeapps_path);
								system(buffer);
						}
						sprintf(buffer, "grep -q x-scheme-handler/about= %s", mimeapps_path);
						if (system(buffer) == 0) {
								sprintf(buffer, "sed -i 's/x-scheme-handler\\/about=.*/x-scheme-handler\\/about=magicurl.desktop/g' %s", mimeapps_path);
								system(buffer);
						} else {
								sprintf(buffer, "sed -i 's/\\[Default Applications\\]/\\[Default Applications\\]\\nx-scheme-handler\\/about=magicurl.desktop/g' %s", mimeapps_path);
								system(buffer);
						}
				} else {
						// If path to mimeapps.list does not exist, create it and then create the file
						sprintf(buffer, "touch %s", mimeapps_path);
						if (system(buffer) != 0) {
								sprintf(buffer, "mkdir -p %s%s", userpath, APPSDIR);
								system(buffer);
								sprintf(buffer, "touch %s", mimeapps_path);
								system(buffer);
						}
						// Insert all lines concerning web mimes
						sprintf(buffer, "echo [Default Applications] >> %s", mimeapps_path);
						system(buffer);
						sprintf(buffer, "echo text/html=magicurl.desktop >> %s", mimeapps_path);
						system(buffer);
						sprintf(buffer, "echo x-scheme-handler/http=magicurl.desktop >> %s", mimeapps_path);
						system(buffer);
						sprintf(buffer, "echo x-scheme-handler/https=magicurl.desktop >> %s", mimeapps_path);
						system(buffer);
						sprintf(buffer, "echo x-scheme-handler/about=magicurl.desktop >> %s", mimeapps_path);
						system(buffer);
				}
		}
		free(kde_path);
		free(mimeapps_path);
#endif /*defined(WIN32) || defined(WIN64)*/
	return result;
}

int MagicUrlHelper::resetStandardBrowser() {
	int result;
	char bc_call[4096];

	snprintf(bc_call, 4095, "\"%s\" -resetregistry", magicUrlPath);
	bc_call[4095] = 0;
	result = launch_program(bc_call, 0, 0, 0, NULL);
	vlog.info("Re-setting standard browser (removing registry keys) using: %s", bc_call);
	return result;
}

int MagicUrlHelper::isUserWantsToSetStandardBrowser() {
	return userWantsToSetStandardBrowser;
}

int MagicUrlHelper::isAskAgainNextTime() {
	return askAgainNextTime;
}

int MagicUrlHelper::isMagicUrlInstalled() {
	return magicUrlInstalled;
}

#if !defined(WIN32) && !defined(WIN64)

void MagicUrlHelper::updateKdeglobals(char *path) {
		if (file_exists(path)) {
				// If there already is a line defining the default browser -> replace it
				sprintf(buffer, "grep -q BrowserApplication\\\\[\\$e\\\\] %s", path);
				if (system(buffer) == 0) {
						sprintf(buffer, "sed -i 's/BrowserApplication\\[\\$e\\]=.*/BrowserApplication\\[\\$e\\]=magicurl.desktop/g' %s", path);
						system(buffer);
				// If there is no default browser defined but the General heading already exists -> insert browser afterwards
				} else {
						sprintf(buffer, "grep -q \\\\[General\\\\] %s", path);
						if (system(buffer) == 0) {
								sprintf(buffer, "sed -i 's/\\[General\\]/\\[General\\]\\nBrowserApplication\\[\\$e\\]=magicurl.desktop/g' %s", path);
								system(buffer);
								// If neither header General nor default browser are defined, append both to end of file
						} else {
								sprintf(buffer, "echo [General] >> %s", path);
								system(buffer);
								sprintf(buffer, "echo BrowserApplication[\\$e]=magicurl.desktop >> %s", path);
								system(buffer);
						}
				 }
		} else {
				// If path to kdeglobals file does not exist, create it and then create the file
				sprintf(buffer, "touch %s", path);
				if (system(buffer) == 0) {
						sprintf(buffer, "mkdir -p %s%s", userpath, KDEDIR);
						if (system(buffer) == 0) {
								sprintf(buffer, "mkdir -p %s%s", userpath, KDEDIR2);
								system(buffer);
						}
						system(buffer);
						sprintf(buffer, "touch %s", path);
						system(buffer);
				}
				// Insert General header and default Browser to empty file
				sprintf(buffer, "echo [General] >> %s", path);
				system(buffer);
				sprintf(buffer, "echo BrowserApplication[\\$e]=magicurl.desktop >> %s", path);
				system(buffer);
		}
}
#endif /*!defined(WIN32) && !defined(WIN64)*/

#if !defined(WIN32) && !defined(WIN64) && !defined(__APPLE__)
char* findOpenMagicurlSocket() {
	const char* tmpDir = "/tmp";
	uid_t currentUid = getuid();
	pid_t currentPid = getpid();

	vlog.debug("%s: Scanning directory '%s' for magicurl sockets", __func__, tmpDir);

	DIR* dir = opendir(tmpDir);
	if (!dir) {
		vlog.error("%s: Failed to open directory '%s': %s", __func__, tmpDir, strerror(errno));
		return NULL;
	}

	struct dirent* entry;
	std::regex magicurlRegex("^\\.magicurl([0-9]+)$");

	while ((entry = readdir(dir)) != NULL) {
		vlog.debug("%s: Inspecting file '%s'", __func__, entry->d_name);

		std::cmatch match;
		if (!std::regex_match(entry->d_name, match, magicurlRegex)) {
			vlog.debug("%s: File '%s' does not match pattern", __func__, entry->d_name);
			continue;
		}

		pid_t socketPid = std::stoi(match[1].str());
		if (socketPid == currentPid) {
			vlog.debug("%s: Skipping file '%s' (current process PID)", __func__, entry->d_name);
			continue;
		}

		std::string filePath = std::string(tmpDir) + "/" + entry->d_name;

		// Check if the file is a socket and belongs to the current user
		struct stat statBuf;
		if (stat(filePath.c_str(), &statBuf) == 0 &&
			S_ISSOCK(statBuf.st_mode) &&
			statBuf.st_uid == currentUid) {
			vlog.debug("%s: File '%s' is a socket and owned by the current user", __func__, filePath.c_str());

			// Check if there's someone listening behind the socket
			int sockFd = socket(AF_UNIX, SOCK_STREAM, 0);
			if (sockFd == -1) {
				vlog.error("%s: Failed to create socket: %s", __func__, strerror(errno));
				continue;
			}

			struct sockaddr_un addr;
			memset(&addr, 0, sizeof(addr));
			addr.sun_family = AF_UNIX;
			strncpy(addr.sun_path, filePath.c_str(), sizeof(addr.sun_path) - 1);

			if (connect(sockFd, (struct sockaddr*)&addr, sizeof(addr)) == 0) {
				close(sockFd);
				closedir(dir);
				vlog.debug("%s: Successfully connected to socket '%s'", __func__, filePath.c_str());
				return strdup(filePath.c_str());
			} else {
				vlog.debug("%s: Failed to connect to socket '%s': %s", __func__, filePath.c_str(), strerror(errno));
			}

			close(sockFd);
		} else {
			vlog.debug("%s: File '%s' is not a valid socket or not owned by the current user", __func__, filePath.c_str());
		}
	}

	closedir(dir);
	vlog.info("%s: No suitable magicurl socket found", __func__);
	return NULL;
}
#endif /* !defined(WIN32) && !defined(WIN64) && !defined(__APPLE__) */

#if !defined(WIN32) && !defined(WIN64) && !defined(__APPLE__)
char* MagicUrlHelper::get_magicurl_socket_path() {
		/* To make it possible that several viewer sessions can be started without using the
		 * same socket und to clean up old sockets, a list of pids of all running viewers is created
		 * and compared to the magicurl socket file names. All files that do not have a name
		 * matching this list are deleted.
		 */
		FILE * output;
		char magicurl_socket_list[512];
		char pidlist[512];
		char *temp1, *temp2;
		char delimiter[] = " ";

		// Get the list of PIDs of tightgateviewer.
		output = popen("pidof tightgateviewer | tr -d '\n'", "r");
		if (!output) {
				vlog.error("tightgateviewer pid not found (failed to run command), giving up");
				return NULL;
		}
		if (!fgets(pidlist, sizeof(pidlist), output)) {
				vlog.error("tightgateviewer pid not found (got an empty list of pid's), giving up");
				pclose(output);
				return NULL;
		}
		pclose(output);
		pidlist[511] ='\0';

		// Get the list of magicurl sockets.
		char *command;
		command = (char *) malloc(strlen(MAGICURL_SOCKET) + 24);
		strcpy(command, "ls /tmp/");
		strcat(command, MAGICURL_SOCKET);
		strcat(command, "* | tr '\n' ' '");

		output = popen(command, "r");
		if(!output) {
				vlog.error("failed to list existing sockets, giving up");
				return NULL;
		}
		if (!fgets(magicurl_socket_list, sizeof(magicurl_socket_list), output)) {
				pclose(output);
		} else {
				pclose(output);
				magicurl_socket_list[511] = '\0';

				// Compare sockets to running processes.
				char *pid;
				char *socket;
				socket = strtok_r(magicurl_socket_list, delimiter, &temp2);
				int found;
				char pidlistcopy[512];

				while (socket != NULL) {
						found = 0;
						strcpy(pidlistcopy, pidlist);
						pid = strtok_r(pidlistcopy, delimiter, &temp1);
						while (pid != NULL) {
								if(strstr(socket, pid)) {
										found = 1;
								}
								pid = strtok_r(NULL, delimiter, &temp1);
						}
						if (found == 0) {
								// Use unlink instead of a system call (like this, we also avoid ownership problems Mantis 4920)
								unlink(socket);
#if 0
								char *remove;
								remove = (char *) malloc(strlen(socket) + 4);
								strcpy(remove, "rm ");
								strcat(remove, socket);
								system(remove);
								free(remove);
#endif
						}
						socket = strtok_r(NULL, delimiter, &temp2);
				}
		}
		const char *directory = "/tmp/";
		char* magicurl_socket_path;
		char buffer[11];
		buffer[10] = '\0';
		snprintf(buffer, 10, "%d", getpid());
		magicurl_socket_path = (char *) malloc(strlen(directory) + strlen(MAGICURL_SOCKET) + strlen(buffer) + 1);
		strcpy(magicurl_socket_path, directory);
		strcat(magicurl_socket_path, MAGICURL_SOCKET);
		strcat(magicurl_socket_path, buffer);

		free(command);
		return magicurl_socket_path;
}
#endif /*!defined(WIN32) || !defined(WIN64) || !defined(__APPLE__)*/


char* MagicUrlHelper::getMagicurlPipeName() {
#ifdef WIN32
	char* tmpMagicurl = get_tmp_magicurl_pipe_name_file_path();
	char* pipeName = NULL;
	vlog.debug("%s: looking for file: %s", __func__, tmpMagicurl);

	FILE* tmpMagicurlFile;
	if ((tmpMagicurlFile = fopen(tmpMagicurl, "r"))) {
		vlog.debug("%s: found magic url temp file (I should be able to read a pipe name from it)", __func__);
		char textLine[4096];
		while (fgets(textLine, 4095, tmpMagicurlFile)) {
			pipeName = strdup(textLine);
			vlog.debug("%s: read pipe name from file: '%s'", __func__, pipeName);
			break; // I just want to read the first line, which has the filename
		}
		fclose(tmpMagicurlFile);
	} else {
		vlog.info("%s: couldn't find file that contains the pipe name. Using standard name.", __func__);
	}

	if (!pipeName) {
		pipeName = MAGICURL_PIPE;
	}
	vlog.debug("%s: pipe name: %s", __func__, pipeName);
	return pipeName;
#else
	vlog.info("%s: function not yet implemented for linux!", __func__); // Possibly doesn't even need to be implemented (for the compiler for now)
	return strdup("");
#endif /* WIN32 */
}



int MagicUrlHelper::sendUrlToViewer(const char* url) {
#ifdef WIN32
	vlog.debug("%s: Trying to send a WindowsMessage (NamedPipe) to a possibly running vnvciewer", __func__);
	char* urlEncoded = url_semi_encode(url);
	if (!urlEncoded) {
		vlog.error("%s: failed to encode url. Can't send url through named pipe", __func__);
		return 0;
	}
	vlog.debug("%s: urlEncoded: %s", __func__, urlEncoded);
	char* magicurlPipeName = getMagicurlPipeName();
	vlog.debug("%s: magicurlPipeName: %s", __func__, magicurlPipeName);

	HANDLE hPipe;
	DWORD dwWritten;
	int succeeded = 0;
	hPipe = CreateFile(TEXT(magicurlPipeName), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
	if (hPipe != INVALID_HANDLE_VALUE) {
		succeeded = WriteFile(hPipe, urlEncoded, strlen(urlEncoded) + 1, &dwWritten, NULL) ? 1 : 0;
		if (succeeded) {
			vlog.debug("Sent (encoded) URL: '%s' through pipe '%s'", urlEncoded, magicurlPipeName);
		}
		CloseHandle(hPipe);
	}
	free(urlEncoded);
	return succeeded;
#else
	vlog.debug("%s: Trying to send a message via UNIX socket to a possibly running viewer", __func__);

	char* urlEncoded = url_semi_encode(url);
	if (!urlEncoded) {
		vlog.error("%s: Failed to encode URL. Can't send URL through UNIX socket", __func__);
		return 0;
	}

	vlog.debug("%s: urlEncoded: %s", __func__, urlEncoded);

	const char* socketPath = findOpenMagicurlSocket();
	if (!socketPath) {
		vlog.info("%s: didn't find a path to an open socket", __func__);
		return 0;
	}
	vlog.debug("%s: socketPath: %s", __func__, socketPath);

	int sockFd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (sockFd == -1) {
		vlog.error("%s: Failed to create socket: %s", __func__, strerror(errno));
		free(urlEncoded);
		return 0;
	}

	struct sockaddr_un serverAddr;
	memset(&serverAddr, 0, sizeof(serverAddr));
	serverAddr.sun_family = AF_UNIX;
	strncpy(serverAddr.sun_path, socketPath, sizeof(serverAddr.sun_path) - 1);

	if (connect(sockFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
		vlog.error("%s: Failed to connect to UNIX socket '%s': %s", __func__, socketPath, strerror(errno));
		close(sockFd);
		free(urlEncoded);
		return 0;
	}

	ssize_t sentBytes = send(sockFd, urlEncoded, strlen(urlEncoded) + 1, 0); // Send URL including null terminator
	if (sentBytes == -1) {
		vlog.error("%s: Failed to send data through UNIX socket: %s", __func__, strerror(errno));
		close(sockFd);
		free(urlEncoded);
		return 0;
	}

	vlog.debug("%s: Sent (encoded) URL: '%s' through UNIX socket '%s'", __func__, urlEncoded, socketPath);

	close(sockFd);
	free(urlEncoded);
	return 1;
#endif /* WIN32 */
}
