diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index e2fac90c..981473fb 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -43,7 +43,9 @@ INCLUDEPATH += $$WALLET_ROOT/include \ $$PWD/src/libwalletqt \ $$PWD/src/QR-Code-generator \ $$PWD/src \ - $$WALLET_ROOT/src + $$WALLET_ROOT/src \ + $$WALLET_ROOT/external/easylogging++ \ + $$WALLET_ROOT/contrib/epee/include HEADERS += \ src/main/filter.h \ @@ -372,11 +374,18 @@ macx { # LIBS+= -Wl,-Bstatic # } - OPENSSL_LIBRARY_DIRS = $$system(brew --prefix openssl, lines, EXIT_CODE) + OPENSSL_DIR = $$system(brew --prefix openssl, lines, EXIT_CODE) + !equals(EXIT_CODE, 0) { + OPENSSL_DIR = /usr/local/ssl + } + OPENSSL_LIBRARY_DIR = $$OPENSSL_DIR/lib + INCLUDEPATH += $$OPENSSL_DIR/include + + BOOST_DIR = $$system(brew --prefix boost, lines, EXIT_CODE) equals(EXIT_CODE, 0) { - OPENSSL_LIBRARY_DIRS = $$OPENSSL_LIBRARY_DIRS/lib + INCLUDEPATH += $$BOOST_DIR/include } else { - OPENSSL_LIBRARY_DIRS = /usr/local/ssl/lib + INCLUDEPATH += /usr/local/include } QT += macextras @@ -386,7 +395,7 @@ macx { LIBS+= -Wl,-bind_at_load LIBS+= \ -L/usr/local/lib \ - -L$$OPENSSL_LIBRARY_DIRS \ + -L$$OPENSSL_LIBRARY_DIR \ -L/usr/local/opt/boost/lib \ -lboost_serialization \ -lboost_thread-mt \ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b8d86661..9cfe114b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -97,6 +97,8 @@ file(GLOB_RECURSE SRC_HEADERS *.h) target_include_directories(monero-gui PUBLIC ${CMAKE_SOURCE_DIR}/monero/include ${CMAKE_SOURCE_DIR}/monero/src + ${CMAKE_SOURCE_DIR}/monero/external/easylogging++ + ${CMAKE_SOURCE_DIR}/monero/contrib/epee/include ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/daemon ${CMAKE_CURRENT_SOURCE_DIR}/libwalletqt diff --git a/src/main/main.cpp b/src/main/main.cpp index 7a454140..c5210e5f 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -437,8 +436,7 @@ int main(int argc, char *argv[]) #endif engine.rootContext()->setContextProperty("builtWithScanner", builtWithScanner); - QNetworkAccessManager *manager = new QNetworkAccessManager(); - Prices prices(manager); + Prices prices; engine.rootContext()->setContextProperty("Prices", &prices); // Load main window (context properties needs to be defined obove this line) diff --git a/src/qt/prices.cpp b/src/qt/prices.cpp index 2f212f4b..6c09553a 100644 --- a/src/qt/prices.cpp +++ b/src/qt/prices.cpp @@ -26,85 +26,85 @@ // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include #include -#include + +// TODO: wallet_merged - epee library triggers the warnings +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wreorder" +#include +#pragma GCC diagnostic pop #include "utils.h" #include "prices.h" - -Prices::Prices(QNetworkAccessManager *networkAccessManager, QObject *parent) - : QObject(parent) { - this->m_networkAccessManager = networkAccessManager; +Prices::Prices(QObject *parent) + : QObject(parent) + , m_scheduler(this) +{ } -void Prices::getJSON(const QString url) { +void Prices::getJSON(const QString url) const +{ qDebug() << QString("Fetching: %1").arg(url); - QNetworkRequest request; - request.setUrl(QUrl(url)); - request.setRawHeader("User-Agent", randomUserAgent().toUtf8()); - request.setRawHeader("Content-Type", "application/json"); - m_reply = this->m_networkAccessManager->get(request); + m_scheduler.run([this, url] { + epee::net_utils::http::http_simple_client http_client; - connect(m_reply, SIGNAL(finished()), this, SLOT(gotJSON())); + const QUrl urlParsed(url); + http_client.set_server(urlParsed.host().toStdString(), urlParsed.scheme() == "https" ? "443" : "80", {}); + + const QString uri = (urlParsed.hasQuery() ? urlParsed.path() + "?" + urlParsed.query() : urlParsed.path()); + const epee::net_utils::http::http_response_info* pri = NULL; + constexpr std::chrono::milliseconds timeout = std::chrono::seconds(15); + + const bool result = http_client.invoke( + uri.toStdString(), + "GET", + {}, + timeout, + std::addressof(pri), + { + {"Content-Type", "application/json; charset=utf-8"}, + {"User-Agent", randomUserAgent().toStdString()} + }); + + if (!result) + { + this->gotError("unknown error"); + } + else if (!pri) + { + this->gotError("internal error (null response ptr)"); + } + else if (pri->m_response_code != 200) + { + this->gotError(QString("response code: %1").arg(pri->m_response_code)); + } + else + { + QJsonDocument doc = QJsonDocument::fromJson({&pri->m_body[0], static_cast(pri->m_body.size())}); + if (doc.isEmpty()) + { + this->gotError("bad JSON"); + } + else + { + // Insert source url for later reference + QJsonObject docobj = doc.object(); + docobj["_url"] = url; + doc.setObject(docobj); + + QVariantMap vMap = doc.object().toVariantMap(); + emit priceJsonReceived(vMap); + } + } + }); } -void Prices::gotJSON() { - // Check connectivity - if (!m_reply || m_reply->error() != QNetworkReply::NoError){ - this->gotError("Problem with reply from server. Check connectivity."); - m_reply->deleteLater(); - return; - } - - // Check json header - QList headerList = m_reply->rawHeaderList(); - QByteArray headerJson = m_reply->rawHeader("Content-Type"); - if(headerJson.length() <= 15){ - this->gotError("Bad Content-Type"); - m_reply->deleteLater(); - return; - } - - QString headerJsonStr = QTextCodec::codecForMib(106)->toUnicode(headerJson); - int _contentType = headerList.indexOf("Content-Type"); - if (_contentType < 0 || !headerJsonStr.startsWith("application/json")){ - this->gotError("Bad Content-Type"); - m_reply->deleteLater(); - return; - } - - // Check valid json document - QByteArray data = m_reply->readAll(); - QJsonDocument doc = QJsonDocument::fromJson(data); - QString jsonString = doc.toJson(QJsonDocument::Indented); - if (jsonString.isEmpty()){ - this->gotError("Bad JSON"); - m_reply->deleteLater(); - return; - } - - // Insert source url for later reference - QUrl url = m_reply->url(); - QJsonObject docobj = doc.object(); - docobj["_url"] = url.toString(); - doc.setObject(docobj); - - qDebug() << QString("Fetched: %1").arg(url.toString()); - - // Emit signal - QVariantMap vMap = doc.object().toVariantMap(); - emit priceJsonReceived(vMap); - - m_reply->deleteLater(); -} - -void Prices::gotError() { - this->gotError("Unknown error"); -} - -void Prices::gotError(const QString &message) { +void Prices::gotError(const QString &message) const +{ qCritical() << "[Fiat API] Error:" << message; emit priceJsonError(message); } diff --git a/src/qt/prices.h b/src/qt/prices.h index 334fd9dc..4168aedc 100644 --- a/src/qt/prices.h +++ b/src/qt/prices.h @@ -3,28 +3,27 @@ #include #include -#include -#include -#include + +#include "FutureScheduler.h" class Prices : public QObject { Q_OBJECT public: - Prices(QNetworkAccessManager *networkAccessManager, QObject *parent = nullptr); + Prices(QObject *parent = nullptr); -public slots: - Q_INVOKABLE void getJSON(const QString url); - void gotJSON(); - void gotError(); - void gotError(const QString &message); -signals: - void priceJsonReceived(QVariantMap document); - void priceJsonError(QString message); +public: + Q_INVOKABLE void getJSON(const QString url) const; private: - mutable QPointer m_reply; - QNetworkAccessManager *m_networkAccessManager; + void gotError(const QString &message) const; + +signals: + void priceJsonReceived(QVariantMap document) const; + void priceJsonError(QString message) const; + +private: + mutable FutureScheduler m_scheduler; }; #endif // PRICES_H