aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMatt Corallo <matt@bluematt.me>2011-12-23 20:27:12 -0800
committerMatt Corallo <matt@bluematt.me>2012-01-05 00:29:28 -0500
commit7d145a0f591dab109eae9adcfaf59303cce0431a (patch)
tree95fbe9a79ca7c5ebf393a7121530c903e37dec38 /src
parent9a93c4c02471fea7acb4882efaebaaefc1733466 (diff)
Add support for opening bitcoin: URIs directly.
Diffstat (limited to 'src')
-rw-r--r--src/init.cpp2
-rw-r--r--src/qt/bitcoin.cpp55
-rw-r--r--src/qt/bitcoingui.cpp7
-rw-r--r--src/qt/bitcoingui.h1
-rw-r--r--src/qt/qtipcserver.cpp95
-rw-r--r--src/qt/qtipcserver.h2
-rw-r--r--src/qt/sendcoinsdialog.cpp10
-rw-r--r--src/qt/sendcoinsdialog.h1
-rw-r--r--src/qtui.h1
9 files changed, 173 insertions, 1 deletions
diff --git a/src/init.cpp b/src/init.cpp
index 8cbf21cc4b..05ba795381 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -272,7 +272,7 @@ bool AppInit2(int argc, char* argv[])
#ifndef QT_GUI
for (int i = 1; i < argc; i++)
- if (!IsSwitchChar(argv[i][0]))
+ if (!IsSwitchChar(argv[i][0]) && !(strlen(argv[i]) > 7 && strncasecmp(argv[i], "bitcoin:", 8) == 0))
fCommandLine = true;
if (fCommandLine)
diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp
index 894bbb9d1e..85ece9e2ce 100644
--- a/src/qt/bitcoin.cpp
+++ b/src/qt/bitcoin.cpp
@@ -8,6 +8,7 @@
#include "headers.h"
#include "init.h"
+#include "qtipcserver.h"
#include <QApplication>
#include <QMessageBox>
@@ -18,6 +19,8 @@
#include <QSplashScreen>
#include <QLibraryInfo>
+#include <boost/interprocess/ipc/message_queue.hpp>
+
// Need a global reference for the notifications to find the GUI
BitcoinGUI *guiref;
QSplashScreen *splashref;
@@ -79,6 +82,22 @@ bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindo
return payFee;
}
+void ThreadSafeHandleURL(const std::string& strURL)
+{
+ if(!guiref)
+ return;
+
+ // Call slot on GUI thread.
+ // If called from another thread, use a blocking QueuedConnection.
+ Qt::ConnectionType connectionType = Qt::DirectConnection;
+ if(QThread::currentThread() != QCoreApplication::instance()->thread())
+ {
+ connectionType = Qt::BlockingQueuedConnection;
+ }
+ QMetaObject::invokeMethod(guiref, "handleURL", connectionType,
+ Q_ARG(QString, QString::fromStdString(strURL)));
+}
+
void CalledSetStatusBar(const std::string& strText, int nField)
{
// Only used for built-in mining, which is disabled, simple ignore
@@ -114,6 +133,25 @@ std::string _(const char* psz)
int main(int argc, char *argv[])
{
+ // Do this early as we don't want to bother initializing if we are just calling IPC
+ for (int i = 1; i < argc; i++)
+ {
+ if (strlen(argv[i]) > 7 && strncasecmp(argv[i], "bitcoin:", 8) == 0)
+ {
+ const char *strURL = argv[i];
+ try {
+ boost::interprocess::message_queue mq(boost::interprocess::open_only, "BitcoinURL");
+ if(mq.try_send(strURL, strlen(strURL), 0))
+ exit(0);
+ else
+ break;
+ }
+ catch (boost::interprocess::interprocess_exception &ex) {
+ break;
+ }
+ }
+ }
+
// Internal string conversion is all UTF-8
QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForTr());
@@ -185,6 +223,23 @@ int main(int argc, char *argv[])
window.show();
}
+ // Place this here as guiref has to be defined if we dont want to lose URLs
+ ipcInit();
+ // Check for URL in argv
+ for (int i = 1; i < argc; i++)
+ {
+ if (strlen(argv[i]) > 7 && strncasecmp(argv[i], "bitcoin:", 8) == 0)
+ {
+ const char *strURL = argv[i];
+ try {
+ boost::interprocess::message_queue mq(boost::interprocess::open_only, "BitcoinURL");
+ mq.try_send(strURL, strlen(strURL), 0);
+ }
+ catch (boost::interprocess::interprocess_exception &ex) {
+ }
+ }
+ }
+
app.exec();
guiref = 0;
diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp
index d4ce8a79e1..c9381a6059 100644
--- a/src/qt/bitcoingui.cpp
+++ b/src/qt/bitcoingui.cpp
@@ -671,6 +671,13 @@ void BitcoinGUI::dropEvent(QDropEvent *event)
event->acceptProposedAction();
}
+void BitcoinGUI::handleURL(QString strURL)
+{
+ gotoSendCoinsPage();
+ QUrl url = QUrl(strURL);
+ sendCoinsPage->handleURL(&url);
+}
+
void BitcoinGUI::setEncryptionStatus(int status)
{
switch(status)
diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h
index a0905e44ad..d54dc9fcd2 100644
--- a/src/qt/bitcoingui.h
+++ b/src/qt/bitcoingui.h
@@ -123,6 +123,7 @@ public slots:
@param[out] payFee true to pay the fee, false to not pay the fee
*/
void askFee(qint64 nFeeRequired, bool *payFee);
+ void handleURL(QString strURL);
private slots:
/** Switch to overview (home) page */
diff --git a/src/qt/qtipcserver.cpp b/src/qt/qtipcserver.cpp
new file mode 100644
index 0000000000..2ed8b915c5
--- /dev/null
+++ b/src/qt/qtipcserver.cpp
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 The Bitcoin developers
+// Distributed under the MIT/X11 software license, see the accompanying
+// file license.txt or http://www.opensource.org/licenses/mit-license.php.
+
+#include <boost/algorithm/string.hpp>
+#include <boost/interprocess/ipc/message_queue.hpp>
+#include <boost/tokenizer.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include "headers.h"
+
+using namespace boost::interprocess;
+using namespace boost::posix_time;
+using namespace boost;
+using namespace std;
+
+void ipcShutdown()
+{
+ message_queue::remove("BitcoinURL");
+}
+
+void ipcThread(void* parg)
+{
+ message_queue* mq = (message_queue*)parg;
+ char strBuf[257];
+ size_t nSize;
+ unsigned int nPriority;
+ loop
+ {
+ ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(100);
+ if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
+ {
+ strBuf[nSize] = '\0';
+ // Convert bitcoin:// URLs to bitcoin: URIs
+ if (strBuf[8] == '/' && strBuf[9] == '/')
+ {
+ for (int i = 8; i < 256; i++)
+ {
+ strBuf[i] = strBuf[i+2];
+ }
+ }
+ ThreadSafeHandleURL(strBuf);
+ Sleep(1000);
+ }
+ if (fShutdown)
+ {
+ ipcShutdown();
+ break;
+ }
+ }
+ ipcShutdown();
+}
+
+void ipcInit()
+{
+ message_queue* mq;
+ char strBuf[257];
+ size_t nSize;
+ unsigned int nPriority;
+ try {
+ mq = new message_queue(open_or_create, "BitcoinURL", 2, 256);
+
+ // Make sure we don't lose any bitcoin: URIs
+ for (int i = 0; i < 2; i++)
+ {
+ ptime d = boost::posix_time::microsec_clock::universal_time() + millisec(1);
+ if(mq->timed_receive(&strBuf, sizeof(strBuf), nSize, nPriority, d))
+ {
+ strBuf[nSize] = '\0';
+ // Convert bitcoin:// URLs to bitcoin: URIs
+ if (strBuf[8] == '/' && strBuf[9] == '/')
+ {
+ for (int i = 8; i < 256; i++)
+ {
+ strBuf[i] = strBuf[i+2];
+ }
+ }
+ ThreadSafeHandleURL(strBuf);
+ }
+ else
+ break;
+ }
+
+ // Make sure only one bitcoin instance is listening
+ message_queue::remove("BitcoinURL");
+ mq = new message_queue(open_or_create, "BitcoinURL", 2, 256);
+ }
+ catch (interprocess_exception &ex) {
+ return;
+ }
+ if (!CreateThread(ipcThread, mq))
+ {
+ delete mq;
+ }
+}
diff --git a/src/qt/qtipcserver.h b/src/qt/qtipcserver.h
new file mode 100644
index 0000000000..1de0334afd
--- /dev/null
+++ b/src/qt/qtipcserver.h
@@ -0,0 +1,2 @@
+void ipcInit();
+void ipcShutdown();
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index 6d32891172..0d9a604d3b 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -30,6 +30,8 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(addEntry()));
connect(ui->clearButton, SIGNAL(clicked()), this, SLOT(clear()));
+
+ fNewRecipientAllowed = true;
}
void SendCoinsDialog::setModel(WalletModel *model)
@@ -92,6 +94,8 @@ void SendCoinsDialog::on_sendButton_clicked()
formatted.append(tr("<b>%1</b> to %2 (%3)").arg(BitcoinUnits::formatWithUnit(BitcoinUnits::BTC, rcp.amount), Qt::escape(rcp.label), rcp.address));
}
+ fNewRecipientAllowed = false;
+
QMessageBox::StandardButton retval = QMessageBox::question(this, tr("Confirm send coins"),
tr("Are you sure you want to send %1?").arg(formatted.join(tr(" and "))),
QMessageBox::Yes|QMessageBox::Cancel,
@@ -99,6 +103,7 @@ void SendCoinsDialog::on_sendButton_clicked()
if(retval != QMessageBox::Yes)
{
+ fNewRecipientAllowed = true;
return;
}
@@ -106,6 +111,7 @@ void SendCoinsDialog::on_sendButton_clicked()
if(!ctx.isValid())
{
// Unlock wallet was cancelled
+ fNewRecipientAllowed = true;
return;
}
@@ -152,6 +158,7 @@ void SendCoinsDialog::on_sendButton_clicked()
accept();
break;
}
+ fNewRecipientAllowed = true;
}
void SendCoinsDialog::clear()
@@ -236,6 +243,9 @@ QWidget *SendCoinsDialog::setupTabChain(QWidget *prev)
void SendCoinsDialog::pasteEntry(const SendCoinsRecipient &rv)
{
+ if (!fNewRecipientAllowed)
+ return;
+
SendCoinsEntry *entry = 0;
// Replace the first entry if it is still unused
if(ui->entries->count() == 1)
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index 82910257f0..847ee8b697 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -43,6 +43,7 @@ public slots:
private:
Ui::SendCoinsDialog *ui;
WalletModel *model;
+ bool fNewRecipientAllowed;
private slots:
void on_sendButton_clicked();
diff --git a/src/qtui.h b/src/qtui.h
index 17fc44e94b..9791ba544d 100644
--- a/src/qtui.h
+++ b/src/qtui.h
@@ -40,6 +40,7 @@ extern int MyMessageBox(const std::string& message, const std::string& caption="
#define wxMessageBox MyMessageBox
extern int ThreadSafeMessageBox(const std::string& message, const std::string& caption, int style=wxOK, wxWindow* parent=NULL, int x=-1, int y=-1);
extern bool ThreadSafeAskFee(int64 nFeeRequired, const std::string& strCaption, wxWindow* parent);
+extern void ThreadSafeHandleURL(const std::string& strURL);
extern void CalledSetStatusBar(const std::string& strText, int nField);
extern void UIThreadCall(boost::function0<void> fn);
extern void MainFrameRepaint();