linuxdcpp-team team mailing list archive
-
linuxdcpp-team team
-
Mailing list archive
-
Message #00517
[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