← Back to team overview

linuxdcpp-team team mailing list archive

[Branch ~linuxdcpp-team/linuxdcpp/trunk] Rev 353: #361735 Improved magnet link support.

 

Merge authors:
  Razzloss (razzloss)
------------------------------------------------------------
revno: 353 [merge]
fixes bug(s): https://launchpad.net/bugs/361735
committer: Razzloss <razzloss@xxxxxxxxx>
branch nick: master
timestamp: Thu 2010-03-11 22:47:16 +0200
message:
  #361735 Improved magnet link support. 
  
  Needs some cleanup and improved error checking still. 
  Also option descriptions should be improved and how localization works with GOption
modified:
  Changelog.txt
  linux/WulforUtil.cc
  linux/WulforUtil.hh
  linux/mainwindow.cc
  linux/mainwindow.hh
  linux/wulfor.cc


--
lp:linuxdcpp
https://code.launchpad.net/~linuxdcpp-team/linuxdcpp/trunk

Your team LinuxDC++ Team is subscribed to branch lp:linuxdcpp.
To unsubscribe from this branch go to https://code.launchpad.net/~linuxdcpp-team/linuxdcpp/trunk/+edit-subscription.
=== modified file 'Changelog.txt'
--- Changelog.txt	2010-02-28 07:47:28 +0000
+++ Changelog.txt	2010-03-11 20:40:41 +0000
@@ -44,6 +44,7 @@
 [2010-01-30] lp#321246: Fixed a crash in debug builds due to bad nick conversion handling. (Steven)
 [2010-02-01] lp#502673: Fixed an issue with the GUI randomly freezing. (Steven)
 [2010-02-28] Added a setting to disable fast hashing method. (Steven)
+[2010-03-11] lp#361735: Improved magnet link support. Support for passing commands to running LinuxDC++ client. (Razzloss)
 
 *** 1.0.3 2009-02-01 ***
 [2008-08-10] lp#256236: Fixed a crash on startup when using auto-open options.

=== modified file 'linux/WulforUtil.cc'
--- linux/WulforUtil.cc	2009-08-23 22:13:35 +0000
+++ linux/WulforUtil.cc	2010-03-11 20:15:35 +0000
@@ -22,6 +22,7 @@
 #include "WulforUtil.hh"
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
+#include <glib.h>
 #include <dcpp/ClientManager.h>
 #include <dcpp/Util.h>
 #include <iostream>
@@ -41,6 +42,7 @@
 std::vector<std::string> WulforUtil::charsets;
 const std::string WulforUtil::magnetSignature = "magnet:?xt=urn:tree:tiger:";
 GtkIconFactory* WulforUtil::iconFactory = NULL;
+CommandLineArgs WulforUtil::startArguments;
 
 vector<int> WulforUtil::splitString(const string &str, const string &delimiter)
 {
@@ -61,6 +63,59 @@
 	return array;
 }
 
+bool WulforUtil::parseArguments(int *argc, char **argv[])
+{
+	gchar* magnet = NULL, *address = NULL;
+	gboolean show = FALSE, refresh = FALSE;
+
+	GOptionEntry entries[] = {
+		{ "magnet", 'm', 0, G_OPTION_ARG_STRING, &magnet, "Search magnet from connected hubs.", NULL },
+		{ "connect", 'c', 0, G_OPTION_ARG_STRING, &address, "Connect to given hub", NULL },
+		{ "show", 's', 0, G_OPTION_ARG_NONE, &show, "Show running instance (or start a new one)", NULL },
+		{ "refresh", 'r', 0, G_OPTION_ARG_NONE, &refresh, "Refresh filelist. LinuxDC++ has to be running for this to have an effect.", NULL },
+		{ NULL }
+	};
+
+	GOptionContext *context;
+	GError* error = NULL;
+	context = g_option_context_new("");
+	g_option_context_add_main_entries(context, entries, NULL);
+	g_option_context_add_group(context, gtk_get_option_group(TRUE));
+	if (!g_option_context_parse(context, argc, argv, &error))
+	{
+		g_print(_("Option parsing failed: %s\n"), error->message);
+		return FALSE;
+	}
+	else
+	{
+		if (show)
+			startArguments.show = true;
+		if (refresh)
+			startArguments.refresh = true;
+		if (magnet)
+			startArguments.magnets.push_back(std::string(magnet));
+		if (address)
+			startArguments.urls.push_back(std::string(address));
+
+		// Handle extra 'file' arguments passed to commandline
+		parseExtraArguments(*argc, *argv);
+	}
+
+	return TRUE;
+}
+
+void WulforUtil::parseExtraArguments(int argc, char* argv[])
+{
+	for (int i = 1; i < argc; i++)	// i = 1 since argv[0] is prgname
+	{
+		string arg(argv[i]);
+		if (isHubURL(arg))
+			startArguments.urls.push_back(arg);
+		else if (isMagnet(arg))
+			startArguments.magnets.push_back(arg);
+	}
+}
+
 string WulforUtil::linuxSeparator(const string &ps)
 {
 	string str = ps;
@@ -299,6 +354,44 @@
 		g_ascii_strncasecmp(text.c_str(), "adcs://", 7) == 0;
 }
 
+const string& WulforUtil::getPipePath()
+{
+	static string pipePath = "";
+	if (pipePath != "")
+		return pipePath;
+
+	// We really can't use Util::getPath(PATH_USER_CONFIG) for this
+	// since it has to stay same even if core hasn't been started.
+	// Though it might be better to have Util::getPath working
+	// without the core start (if it doesn't work already ==>
+	// should be checked)
+
+	char *home = getenv("HOME");
+	string configPath = home ? string(home) + "/.dc++/" : "/tmp/";
+	pipePath = configPath + "linuxdcpp.pipe";
+	return pipePath;
+}
+
+bool WulforUtil::writeIPCCommand(string cmd)
+{
+	if (cmd.length() < 1)
+		return FALSE;
+
+	const std::string pipepath = WulforUtil::getPipePath();
+	if (cmd[cmd.length() -1] != '\n')
+		cmd += '\n';
+
+	int fd = open(pipepath.c_str(), O_WRONLY);
+	if (fd >= 0)
+	{
+		write(fd, cmd.c_str(), cmd.length());
+		close(fd);
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
 bool WulforUtil::profileIsLocked()
 {
 	static bool profileIsLocked = false;

=== modified file 'linux/WulforUtil.hh'
--- linux/WulforUtil.hh	2009-08-23 22:13:35 +0000
+++ linux/WulforUtil.hh	2010-03-10 21:37:51 +0000
@@ -28,6 +28,13 @@
 #include <dcpp/CID.h>
 #include <dcpp/User.h>
 
+struct CommandLineArgs {
+	std::vector<std::string> magnets;
+	std::vector<std::string> urls;
+	bool show;
+	bool refresh;
+};
+
 class WulforUtil
 {
 	public:
@@ -60,9 +67,14 @@
 		static GtkTreeIter copyRow_gui(GtkTreeStore *store, GtkTreeIter *fromIter, GtkTreeIter *parent = NULL, int position = -1);
 		static void copyValue_gui(GtkTreeStore* store, GtkTreeIter *fromIter, GtkTreeIter *toIter, int position);
 		static void registerIcons();
+		static const std::string& getPipePath();
+		static bool writeIPCCommand(std::string cmd);
 
 		static const std::string ENCODING_LOCALE;
 		
+		static bool parseArguments(int *argc, char **argv[]);
+		static void parseExtraArguments(int argc, char* argv[]);
+		static CommandLineArgs startArguments;
 	private:
 		static std::vector<std::string> charsets;
 		static const std::string magnetSignature;

=== modified file 'linux/mainwindow.cc'
--- linux/mainwindow.cc	2009-11-02 06:01:55 +0000
+++ linux/mainwindow.cc	2010-03-11 20:40:41 +0000
@@ -50,6 +50,10 @@
 #include "wulformanager.hh"
 #include "WulforUtil.hh"
 
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
 using namespace std;
 using namespace dcpp;
 
@@ -152,6 +156,32 @@
 	loadIcons_gui();
 	showTransfersPane_gui();
 
+	// Set up our IPC 
+	const string pipepath = WulforUtil::getPipePath();
+	dcdebug("MainWindow::MainWindow: Pipepath %s\n", pipepath.c_str());
+	int status = mkfifo(pipepath.c_str(), S_IRWXU); 
+	if (status == -1 && errno == EEXIST)
+	{
+		dcdebug("Stale pipe detected. Unlinking...\n");
+		unlink(pipepath.c_str());
+	}
+
+	if (status == 0 || mkfifo(pipepath.c_str(), S_IRWXU) == 0)
+	{
+		int fd = open(pipepath.c_str(), O_NONBLOCK | O_RDWR);
+		if (fd >= 0) 
+		{
+			IPC = g_io_channel_unix_new(fd);	// Eh, can this fail?
+			g_io_add_watch(IPC, G_IO_IN, onExternalData, this);
+			g_io_add_watch(IPC, G_IO_PRI, onExternalData, this);
+		}
+	} 
+	else
+	{
+		dcdebug("MainWindow::MainWindow, mkfifo FAILED!\n");
+		IPC = NULL;
+	}
+
 	// Putting this after all the resizing and moving makes the window appear
 	// in the correct position instantly, looking slightly more cool 
 	// (seems we have rather poor standards for cool?)
@@ -165,6 +195,14 @@
 
 MainWindow::~MainWindow()
 {
+	if (IPC != NULL) {
+		int fd = g_io_channel_unix_get_fd(IPC);
+		g_io_channel_shutdown(IPC, FALSE, NULL);
+		g_io_channel_unref(IPC);
+		close(fd);
+		unlink(WulforUtil::getPipePath().c_str());
+	}
+
 	QueueManager::getInstance()->removeListener(this);
 	TimerManager::getInstance()->removeListener(this);
 	LogManager::getInstance()->removeListener(this);
@@ -219,6 +257,7 @@
 	WulforManager::get()->dispatchClientFunc(f0);
 
 	autoOpen_gui();
+	handleCommandlineArguments_gui();
 }
 
 void MainWindow::setTitle(const string& text)
@@ -1245,6 +1284,107 @@
 	return filename;
 }
 
+void MainWindow::handleCommandlineArguments_gui()
+{
+	std::vector<std::string>::iterator it = WulforUtil::startArguments.magnets.begin();
+	for (; it != WulforUtil::startArguments.magnets.end(); ++it)
+	{
+		string name;
+		int64_t size;
+		string tth;
+
+		if (WulforUtil::splitMagnet(*it, name, size, tth))
+		{
+			Search *s = addSearch_gui();
+			s->putValue_gui(tth, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
+		}
+	}
+
+	for (it = WulforUtil::startArguments.urls.begin(); it != WulforUtil::startArguments.urls.end(); ++it)
+	{
+
+		showHub_gui(*it);
+	}
+}
+
+gboolean MainWindow::onExternalData(GIOChannel* source, GIOCondition cond, gpointer data)
+{
+	MainWindow* mw = (MainWindow*)data;
+	if (mw->IPC != source)
+		return TRUE;
+
+	if (cond != G_IO_IN && cond != G_IO_PRI)
+	{
+		if (cond == G_IO_HUP)
+			dcdebug("G_IO_HUP\n");
+		dcdebug("Weird things in IO Channel\n");
+		return FALSE;
+	}
+
+
+	/* Currently this doesn't handle the situation where data is written
+	 * to pipe without newline. Apparently nothing is read in that case
+	 * and onExternalData is called again and again (causing high cpu usage).
+	 * So I guess the channel should be cleared somehow. Or this crashes (possibly
+	 * fixed the crash...) */
+	gchar *str;
+	gsize len = 0;
+	gsize termpos = 0;
+	GIOStatus status = g_io_channel_read_line(source, &str, &len, &termpos, NULL);
+	if (termpos > 0)
+		str[termpos] = 0;	// replace newline with 0
+	dcdebug("MainWindow::onExternalData, from IO Channel: %s (%d)\n", str,  len);
+	if (status == G_IO_STATUS_NORMAL && len > 0) 
+	{
+		// Handle command
+		string cmd(str);
+		string arg;
+		size_t pos = cmd.find(" ");
+		g_free(str);
+
+		if (pos != string::npos && cmd.length() > pos + 1) 
+		{
+			arg = cmd.substr(pos + 1);
+			cmd = cmd.substr(0, pos);
+		}
+
+		if (cmd == "show") {
+			/* @TODO: Find code which brings the window to current workspace. Instead of just 
+			 * setting it to visible. */
+			gtk_widget_show(GTK_WIDGET(mw->window));
+		} 
+		else if (cmd == "refresh") 
+		{
+			typedef Func0<MainWindow> F0;
+			F0 *func = new F0((MainWindow *)data, &MainWindow::refreshFileList_client);
+			WulforManager::get()->dispatchClientFunc(func);
+		} 
+		else if (cmd == "magnet")
+		{
+			string name;
+			int64_t size;
+			string tth;
+
+			if (WulforUtil::splitMagnet(arg, name, size, tth))
+			{
+				Search *s = mw->addSearch_gui();
+				s->putValue_gui(tth, 0, SearchManager::SIZE_DONTCARE, SearchManager::TYPE_TTH);
+			}
+		}
+		else if (cmd == "connect")
+		{
+			mw->showHub_gui(arg);
+		}
+	}
+	else if (status != G_IO_STATUS_AGAIN)
+	{
+		dcdebug("Weird things in IO Channel\n");
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
 void MainWindow::on(LogManagerListener::Message, time_t t, const string &message) throw()
 {
 	typedef Func2<MainWindow, string, time_t> F2;

=== modified file 'linux/mainwindow.hh'
--- linux/mainwindow.hh	2009-10-11 03:17:46 +0000
+++ linux/mainwindow.hh	2010-03-10 20:22:21 +0000
@@ -34,6 +34,7 @@
 #include "entry.hh"
 #include "treeview.hh"
 #include "transfers.hh"
+#include <glib.h>
 
 class BookEntry;
 class Search;
@@ -75,6 +76,7 @@
 		Search *addSearch_gui();
 		void setMainStatus_gui(std::string text, time_t t = time(NULL));
 		void showNotification_gui(std::string title, std::string body);
+		void handleCommandlineArguments_gui();
 
 		// Client functions
 		void openOwnList_client(bool useSetting);
@@ -131,6 +133,7 @@
 		static void onStatusIconActivated_gui(GtkStatusIcon *statusIcon, gpointer data);
 		static void onStatusIconPopupMenu_gui(GtkStatusIcon *statusIcon, guint button, guint time, gpointer data);
 		static void onShowInterfaceToggled_gui(GtkCheckMenuItem *item, gpointer data);
+		static gboolean onExternalData(GIOChannel* source, GIOCondition cond, gpointer data);
 
 		// Client functions
 		void autoConnect_client();
@@ -149,6 +152,8 @@
 		int64_t lastUpdate, lastUp, lastDown;
 		int emptyStatusWidth;
 		bool minimized;
+
+		GIOChannel* IPC;
 };
 
 #else

=== modified file 'linux/wulfor.cc'
--- linux/wulfor.cc	2009-03-12 05:47:55 +0000
+++ linux/wulfor.cc	2010-03-11 20:40:41 +0000
@@ -44,17 +44,48 @@
 	textdomain("linuxdcpp");
 	bind_textdomain_codeset("linuxdcpp", "UTF-8");
 
+	if (!WulforUtil::parseArguments(&argc, &argv))
+	{
+		return -1;
+	}
+
 	// Check if profile is locked
 	if (WulforUtil::profileIsLocked())
 	{
-		gtk_init(&argc, &argv);
-		std::string message = _("Only one instance of LinuxDC++ is allowed per profile");
-
-		GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message.c_str());
-		gtk_dialog_run(GTK_DIALOG(dialog));
-		gtk_widget_destroy(dialog);
-
-		return -1;
+		bool commandGiven = WulforUtil::startArguments.refresh;
+		if (WulforUtil::startArguments.refresh)
+			WulforUtil::writeIPCCommand("refresh");
+		for (std::vector<std::string>::iterator it = WulforUtil::startArguments.magnets.begin();
+			it != WulforUtil::startArguments.magnets.end(); ++it)
+		{
+			WulforUtil::writeIPCCommand(std::string("magnet ") + *it);
+			commandGiven = TRUE;
+		}
+		for (std::vector<std::string>::iterator it = WulforUtil::startArguments.urls.begin();
+			it != WulforUtil::startArguments.urls.end(); ++it)
+		{
+			WulforUtil::writeIPCCommand(std::string("connect ") + *it);
+			commandGiven = TRUE;
+		}
+
+		if (WulforUtil::startArguments.show) 
+		{
+			return WulforUtil::writeIPCCommand("show");
+		}
+
+		if (!commandGiven) 	// If no arguments were given show the profile lock error. Should we make show the default and remove the lock error message?
+		{			// (@TODO: For that 'show' should really bring the window to visible area instead of just calling gtk_show on it...")
+			gtk_init(&argc, &argv);
+			std::string message = _("Only one instance of LinuxDC++ is allowed per profile");
+
+			GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", message.c_str());
+			gtk_dialog_run(GTK_DIALOG(dialog));
+			gtk_widget_destroy(dialog);
+
+			return -1;
+		}
+
+		return 0;
 	}
 
 	// Start the DC++ client core