I've been looking around on the web on how to create an authentication page when my Qt desktop app opens. I already built the app; that is pretty small and only composed of a MainWindow called from main.cpp.
Now I'd like to add an authentication page when the user opens the app. I created a Google API (following the instruction from this link: http://blog.qt.io/blog/2017/01/25/connecting-qt-application-google-services-using-oauth-2-0/); but it is really incomplete. And looking on the web, I wasn't able to find a single link that gives a working example where:
- The user runs the app and gets asked for his username and password;
- And if it doesn't exist yet, he can create one.
All I've found is incomplete piece of code like the link I shared above; or tutorial that shows how to create a login page with hard-coded passwords and usernames (this is not what I want, I want people to be able to add themselves dynamically based of the Google API).
So please, if someone has a little piece of code where the user gets asked for their username and password, with the code managing the request to the API, that would be great!
EDIT: Adding my code
I'm adding the code of my class GoogleGateway (inspired from what I found here: How to set redirect_uri using QOAuth2AuthorizationCodeFlow and QOAuthHttpServerReplyHandler)
GoogleGateway.h:
#ifndef GOOGLEGATEWAY_H
#define GOOGLEGATEWAY_H
#include <QObject>
class GoogleGateway : public QObject
{
Q_OBJECT
public:
GoogleGateway();
};
#endif // GOOGLEGATEWAY_H
GoogleGateway.cpp:
#include "googlegateway.h"
#include <QApplication>
#include <QObject>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QString>
#include <QFile>
#include <QUrl>
#include <QOAuth2AuthorizationCodeFlow>
#include <QOAuthHttpServerReplyHandler>
#include <QDesktopServices>
GoogleGateway::GoogleGateway() :
QObject(){
auto google = new QOAuth2AuthorizationCodeFlow;
google->setScope("email");
this->connect(google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);
QString val;
QFile file;
file.setFileName("/.../auth.json");
file.open(QIODevice::ReadOnly | QIODevice::Text);
val = file.readAll();
file.close();
QJsonDocument document = QJsonDocument::fromJson(val.toUtf8());
QJsonObject object = document.object();
const auto settingsObject = object["web"].toObject();
const QUrl authUri(settingsObject["auth_uri"].toString());
const auto clientId = settingsObject["client_id"].toString();
const QUrl tokenUri(settingsObject["token_uri"].toString());
const auto clientSecret(settingsObject["client_secret"].toString());
const auto redirectUris = settingsObject["redirect_uris"].toArray();
const QUrl redirectUri(redirectUris[0].toString());
const auto port = static_cast<quint16>(redirectUri.port());
google->setAuthorizationUrl(authUri);
google->setClientIdentifier(clientId);
google->setAccessTokenUrl(tokenUri);
google->setClientIdentifierSharedKey(clientSecret);
auto replyHandler = new QOAuthHttpServerReplyHandler(port, this);
google->setReplyHandler(replyHandler);
google->grant();
}
Now, what do I need to do in my MainWindow.cpp to prompt a login page that will use the class GoogleGateway? Does the class GoogleGateway look good as it is? Or do I need to modify something?
Also, I created an instance of the class GoogleGateway in my MainWindow constructor. And When I run the code, it opens a web tab in my Chrome but throws the Error 400 saying "Error: redirect_uri_mismatch". What does that mean?
Thanks for your help.
So, the article from Qt blog is almost complete. You just need to connect granted signal and make needed requests after that.
Note about /cb path in redirect_uri in Qt blog is not valid anymore(article came about a year ago).
NOTE: The path “/cb” is mandatory in the current
QOAuthHttpServerReplyHandler implementation.
So if you see access error from google after running your code, just copy-paste redirect_uri from there into GoogleConsole Authorized redirect URIs of your configured client. http://localhost:8080/ <- don't forget slash at the end
P.S.: Don't forget to dowload json file with credentials from console again.
Also if you want to call any Google APIs after authorization, you need to enable them in GoogleConsole for your project. To test the code just enable Google+ API
That's it. Here is the complete and working code. (Tested on Linux and Qt 5.10, Don't have Windows at the moment, can't test it there)
googlegateway.h
#ifndef GOOGLEGATEWAY_H
#define GOOGLEGATEWAY_H
#include <QObject>
#include <QOAuth2AuthorizationCodeFlow>
#include <QNetworkReply>
class GoogleGateway : public QObject
{
Q_OBJECT
public:
explicit GoogleGateway(QObject *parent = nullptr);
private:
QOAuth2AuthorizationCodeFlow * google;
};
#endif // GOOGLEGATEWAY_H
googlegateway.cpp
#include "googlegateway.h"
#include <QObject>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QString>
#include <QFile>
#include <QDir>
#include <QUrl>
#include <QOAuthHttpServerReplyHandler>
#include <QDesktopServices>
GoogleGateway::GoogleGateway(QObject *parent) : QObject(parent)
{
this->google = new QOAuth2AuthorizationCodeFlow(this);
this->google->setScope("email");
connect(this->google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);
QByteArray val;
QFile file;
file.setFileName(QDir::toNativeSeparators("/full/path/client_secret_XXXXXXX.apps.googleusercontent.com.json"));
if(file.open(QIODevice::ReadOnly | QIODevice::Text))
{
val = file.readAll();
file.close();
}
QJsonDocument document = QJsonDocument::fromJson(val);
QJsonObject object = document.object();
const auto settingsObject = object["web"].toObject();
const QUrl authUri(settingsObject["auth_uri"].toString());
const auto clientId = settingsObject["client_id"].toString();
const QUrl tokenUri(settingsObject["token_uri"].toString());
const auto clientSecret(settingsObject["client_secret"].toString());
const auto redirectUris = settingsObject["redirect_uris"].toArray();
const QUrl redirectUri(redirectUris[0].toString());
const auto port = static_cast<quint16>(redirectUri.port());
this->google->setAuthorizationUrl(authUri);
this->google->setClientIdentifier(clientId);
this->google->setAccessTokenUrl(tokenUri);
this->google->setClientIdentifierSharedKey(clientSecret);
auto replyHandler = new QOAuthHttpServerReplyHandler(port, this);
this->google->setReplyHandler(replyHandler);
this->google->grant();
connect(this->google, &QOAuth2AuthorizationCodeFlow::granted, [=](){
qDebug() << __FUNCTION__ << __LINE__ << "Access Granted!";
auto reply = this->google->get(QUrl("https://www.googleapis.com/plus/v1/people/me"));
connect(reply, &QNetworkReply::finished, [reply](){
qDebug() << "REQUEST FINISHED. Error? " << (reply->error() != QNetworkReply::NoError);
qDebug() << reply->readAll();
});
});
}
Please remember to set Redirect URI in google console to http://127.0.0.1:port_no/ instead of http://localhost:port_no/ for all localhost.
Remember to put the trailing '/' in the Redirect URI.
Rest of the code
this->google = new QOAuth2AuthorizationCodeFlow(this);
this->google->setScope("email");
connect(this->google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);
this->google->setAuthorizationUrl(QUrl("https://accounts.google.com/o/oauth2/auth"));
this->google->setClientIdentifier(MY_CLIENT_ID);
this->google->setAccessTokenUrl(QUrl("https://oauth2.googleapis.com/token"));
this->google->setClientIdentifierSharedKey(MYTOKEN);
// In my case, I have hardcoded 8080 to test
auto replyHandler = new QOAuthHttpServerReplyHandler(8080, this);
this->google->setReplyHandler(replyHandler);
this->google->grant();
qDebug() << "Access";
connect(this->google, &QOAuth2AuthorizationCodeFlow::granted, [=](){
qDebug() << __FUNCTION__ << __LINE__ << "Access Granted!";
auto reply = this->google->get(QUrl("https://www.googleapis.com/plus/v1/people/me"));
connect(reply, &QNetworkReply::finished, [reply](){
qDebug() << "REQUEST FINISHED. Error? " << (reply->error() != QNetworkReply::NoError);
qDebug() << reply->readAll();
});
});
Related
I'm trying to implement autofill functionality in a Qt Webengine based app.
My approach is based off Viper browser's implementation: running script on page's loadFinished signal. The script run just looks up all the fields that can be populated and fills them as simple as elem.value = 'value'.
It works with simple example like e.g. my wifi router config page (which uses very simple logic for forms). But it doesn't work with e.g. https://onlyfans.com/ login page.
When I run my script I get JS errors like:
"js: Uncaught TypeError: Cannot set property 'value' of null".
The minimal example demonstrating the issue:
#include <QApplication>
#include <QObject>
#include <QTimer>
#include <QWebEngineView>
#include <QWebEnginePage>
#include <QWebEngineProfile>
#include <QWebEngineScript>
#include <QWebEngineScriptCollection>
const char* URL_ONLYFANS = "https://onlyfans.com/";
const char* URL_ROUTER = "http://192.168.0.1/";
const char* SCRIPT_ROUTER(R"(
document.querySelector('input[type=text]').value = 'admin';
document.querySelector('input[type=password]').value = 'mypass';
)");
const char* SCRIPT_ONLYFANS(R"(
document.querySelector('input[name=email]').value = "random#gmail.com";
document.querySelector('input[name=password]').value = "randompass";
)");
class Observer : public QObject
{
Q_OBJECT
public:
explicit Observer(QWebEnginePage *page): _page(page)
{}
public slots:
void onPageLoad(bool ok)
{
qDebug() << "Page loaded" << ok;
// Running script right away pretty much guarantees JS errors
// No JS errors if delay is >= 1500 ms:
QTimer::singleShot(1500, this, &Observer::runScript);
qDebug() << "Scheduled running script";
}
void runScript()
{
_page->runJavaScript(SCRIPT_ONLYFANS);
qDebug() << "Ran script";
}
private:
QWebEnginePage *_page{nullptr};
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWebEngineView view;
QWebEngineProfile profile;
QWebEnginePage page(&profile);
view.setPage(&page);
view.setUrl(QUrl(URL_ONLYFANS));
view.resize(1024, 750);
Observer observer(view.page());
QObject::connect(view.page(), &QWebEnginePage::loadFinished,
&observer, &Observer::onPageLoad);
/*
{
QWebEngineScript script;
script.setName("autofill");
script.setSourceCode(SCRIPT_ONLYFANS);
// Also tried `DocumentCreation` and `Deferred`, didn't work for Onlyfans
script.setInjectionPoint(QWebEngineScript::DocumentReady);
script.setRunsOnSubFrames(true);
script.setWorldId(QWebEngineScript::ApplicationWorld);
view.page()->scripts().insert(script);
}
*/
view.show();
return app.exec();
}
#include "main.moc"
As can be seen I tried to insert my scripts in two ways: on page's loadFinished signal (both immediately and with a time delay) and by inserting my script into page's scripts collection. I tried all available insertion points: DocumentCreation, DocumentReady and Deferred.
In all cases I get JS error shown above. Except for the case when I delay script execution by about 1500 ms, which is obviously a guess and is not robust.
I do understand that the elements I'm trying to set are probably not yet created, so I'd like some advice on how to properly time the script execution.
I have a simple program that should retrieve the HTML from a website URL.
main.cpp
#include "Downloader.h"
#include <QCoreApplication>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
auto dl = new Downloader(&a);
QString url = "https://www.dognow.at/ergebnisse/?page=1";
dl->fetch(url);
return a.exec();
}
Downloader.h
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QNetworkReply>
#include <QObject>
class Downloader : public QObject
{
Q_OBJECT
public:
explicit Downloader(QObject* parent = nullptr);
void fetch(QString &url);
private:
QNetworkAccessManager* m_manager;
private slots:
void replyFinished(QNetworkReply* rep);
};
#endif // DOWNLOADER_H
Downloader.cpp
#include "Downloader.h"
#include <QDebug>
Downloader::Downloader(QObject* parent): QObject(parent),
m_manager(new QNetworkAccessManager(parent))
{}
void Downloader::fetch(QString& url)
{
qDebug() << "fetch " << url;
connect(m_manager, &QNetworkAccessManager::finished, this, &Downloader::replyFinished);
m_manager->get(QNetworkRequest(QUrl(url)));
}
void Downloader::replyFinished(QNetworkReply* rep)
{
QByteArray data=rep->readAll();
QString str(data);
qDebug() << "data len: " << str.length();
rep->close();
}
When I run the program on my local PC it works fine. When I run it on another machine the reply data is empty. On both systems I use Linux (x86_64) and Qt 5.15.0.
I hope someone can give me a hint where I should have a look at.
UPDATE: 2022-04-04 - 16:22:
when I run a simple curl command on the failing machine it works fine.
Ok, I found the problem.
On the failing machin I have an older ubuntu (16.04 LTS) running with an incompatible openssl version.
I found it out because I copied my own Qt libs build (debug) to the other machine and I got SSL error (incompatbile version).
I installed a newer openssl version and know it works.
I'm trying to configure communication with Google OAuth 2.0 in my Qt project. I was using this tutorial, although it seems to be a bit outdated. I configured everything in Google APIs site, used those data in Credentials/OAuth 2.0 Client IDs page:
Header file:
#ifndef GOOGLEAUTH_H
#define GOOGLEAUTH_H
#include <QObject>
#include <QOAuth2AuthorizationCodeFlow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QUrl>
#include <QUrlQuery>
#include <QOAuthHttpServerReplyHandler>
#include <QDesktopServices>
class GoogleAuth : public QObject
{
Q_OBJECT
public:
explicit GoogleAuth(QObject *parent = nullptr);
Q_INVOKABLE void click();
private:
QOAuth2AuthorizationCodeFlow google;
};
#endif // GOOGLEAUTH_H
Source file:
#include "GoogleAuthenticator.h"
GoogleAuth::GoogleAuth(QObject *parent) : QObject(parent)
{
google.setScope("email");
connect(&google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [=](QUrl url) {
QUrlQuery query(url);
query.addQueryItem("prompt", "consent");
query.addQueryItem("access_type", "offline");
query.addQueryItem("nonce", "123456");
url.setQuery(query);
QDesktopServices::openUrl(url);
});
google.setAuthorizationUrl(QUrl("https://accounts.google.com/o/oauth2/auth"));
google.setAccessTokenUrl(QUrl("https://oauth2.googleapis.com/token"));
google.setClientIdentifier("<client_id>");
google.setClientIdentifierSharedKey("<client_secret>");
auto replyHandler = new QOAuthHttpServerReplyHandler(5476, this);
google.setReplyHandler(replyHandler);
connect(&google, &QOAuth2AuthorizationCodeFlow::granted, [=]() {
qDebug() << "Access Granted!";
});
}
void GoogleAuth::click()
{
google.grant();
}
When I run click() method browser opens, I can log into Google account and then it redirects me to the page with following message:
Callback received. Feel free to close this page.
I can even see the code I need in the URL (at least I think it is that code?).
The problem is I don't get proper callback in Qt app. When above page with callback message is loaded, I just get this output in Qt Creator:
qt.networkauth.oauth2: Unexpected call
qt.networkauth.replyhandler: Error transferring https://oauth2.googleapis.com/token - server replied: Bad Request
Outside the app it seems to be working, I checked it on this page.
How to solve that propblem? Can I get more detailed info about that bad request?
I have tested the example and strangely it does not work for the "email" scope, after analyzing the http request I found that the problem is the encoding of the "code" received and that it is used to obtain the token. So my solution is to correct that parameter and that can be done override the requestAccessToken() method or use setModifyParametersFunction(), in this case use the latter:
google.setModifyParametersFunction([](QAbstractOAuth::Stage stage,
QVariantMap* parameters)
{
if(stage == QAbstractOAuth::Stage::RequestingAccessToken){
QByteArray code = parameters->value("code").toByteArray();
(*parameters)["code"] = QUrl::fromPercentEncoding(code);
}
});
The official tutorial is quite outdated, and it contains several problems like the one you're facing. Specifically, you need to URL-decode the login code that Google sends, but you need to intercept the parameter from inside Qt, which requires installing a parameter handler:
googleSSO.setModifyParametersFunction([](QAbstractOAuth::Stage loginStage, QVariantMap* parameters) {
// Percent-decode the "code" parameter so Google can match it
if (QAbstractOAuth::Stage::RequestingAccessToken == loginStage) {
QByteArray code = parameters->value("code").toByteArray();
(*parameters)["code"] = QUrl::fromPercentEncoding(code);
}
});
We wrote about how to authenticate a Qt app with Google SSO, and all the issues that we faced, in our blog.
Answer of Pablo is outdated too. Working solution for now is:
void GoogleCloudAuth::modifyParametersFunction(QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* parameters)
{
qDebug() << "modifyParametersFunction stage=" << static_cast<int>(stage);
if (stage == QAbstractOAuth::Stage::RequestingAuthorization)
{
// The only way to get refresh_token from Google Cloud
parameters->insert("access_type", "offline");
parameters->insert("prompt", "consent");
}
else if (stage == QAbstractOAuth::Stage::RequestingAccessToken)
{
// Percent-decode the "code" parameter so Google can match it
QByteArray code = parameters->value("code").toByteArray();
parameters->replace("code", QUrl::fromPercentEncoding(code));
}
}
Is there any cross platform way to get the current username in a Qt C++ program?
I've crawled the internet and the documentation for a solution, but the only thing I find are OS dependent system calls.
I was actually thinking about it a couple of days ago, and I came to the conclusion of having different alternatives, each with its own trade-off, namely:
Environment variables using qgetenv.
The advantage of this solution would be that it is really easy to implement. The drawback is that if the environment variable is set to something else, this solution is completely unreliable then.
#include <QString>
#include <QDebug>
int main()
{
QString name = qgetenv("USER");
if (name.isEmpty())
name = qgetenv("USERNAME");
qDebug() << name;
return 0;
}
Home location with QStandardPaths
The advantage is that, it is relatively easy to implement, but then again, it can go unreliable easily since it is valid to use different username and "entry" in the user home location.
#include <QStandardPaths>
#include <QStringList>
#include <QDebug>
#include <QDir>
int main()
{
QStringList homePath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
qDebug() << homePath.first().split(QDir::separator()).last();
return 0;
}
Run external processes and use platform specific APIs
This is probably the most difficult to implement, but on the other hand, this seems to be the most reliable as it cannot be changed under the application so easily like with the environment variable or home location tricks. On Linux, you would use QProcess to invoke the usual whoami command, and on Windows, you would use the GetUserName WinAPI for this purpose.
#include <QCoreApplication>
#include <QProcess>
#include <QDebug>
int main(int argc, char **argv)
{
// Strictly pseudo code!
#ifdef Q_OS_WIN
char acUserName[MAX_USERNAME];
DWORD nUserName = sizeof(acUserName);
if (GetUserName(acUserName, &nUserName))
qDebug << acUserName;
return 0;
#elif Q_OS_UNIX
QCoreApplication coreApplication(argc, argv);
QProcess process;
QObject::connect(&process, &QProcess::finished, [&coreApplication, &process](int exitCode, QProcess::ExitStatus exitStatus) {
qDebug() << process.readAllStandardOutput();
coreApplication.quit();
});
process.start("whoami");
return coreApplication.exec();
#endif
}
Summary: I would personally go for the last variant since, even though it is the most difficult to implement, that is the most reliable.
There is no way to get the current username with Qt.
However, you can read this links :
http://www.qtcentre.org/threads/12965-Get-user-name
http://qt-project.org/forums/viewthread/11951
I think the best method is :
#include <stdlib.h>
getenv("USER"); ///for MAc or Linux
getenv("USERNAME"); //for windows
EDIT : You can use qgetenv instead of getenv.
In QT5 and up it is possible to do the following :
QString userName = QDir::home().dirName();
`QDir::home() returns the user's home directory.
You can use qEnvironmentVariable
QString sysUsername = qEnvironmentVariable("USER");
if (sysUsername.isEmpty()) sysUsername = qEnvironmentVariable("USERNAME");
Also you can use QProcessEnvironment like this:
QProcessEnvironmentenv = QProcessEnvironment::systemEnviroment();
QString username = env.value("USER");
There is a way to get the current windows username with Qt. Here it is
mainwindow.ui
This is the form ui
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QProcess>
#include <QDir>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
this->getUser();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::getUser()
{
QProcess *username = new QProcess();
QStringList cmdParamaters, split;
QString clean1, clean2, clean3,userName;
int cutOff, strLen;
cmdParamaters << "/c"<<"\"%USERPROFILE%\"";
username->setProcessChannelMode(QProcess::MergedChannels);
username->start("cmd.exe",cmdParamaters);
username->waitForFinished();
QString vusername (username->readAllStandardOutput());
cutOff = vusername.indexOf("'", 1);
ui->label_2->setText(vusername);
clean1 = vusername.left(cutOff);
ui->label_3->setText(clean1);
clean2 = clean1.remove(0,3);
strLen = clean2.length();
ui->label_4->setText(clean2);
clean3 = clean2.left(strLen-2);
split = clean3.split("\\");
userName = split[2]; //This is the current system username
ui->label_5->setText(userName);
delete username;
}
Output:
Code output
I've recently began the tedious process of modularising a large, monolithic audio player application that I wrote roughly 2 months ago.
This process is going reasonably well, although it appears that one of the methods (ScrobbleMedia - which predictably enough makes HTTP requests to submit information about a playing track to last.fm) no longer seems to make network requests.
However, the QUrl object that would be passed through to the QNetworkAccessManager instance/QNetworkRequest is being built correctly.
For comparison, a functional Mercurial revision of the code is available on BitBucket.
The ScrobbleMedia method currently looks like this, after refactoring:
#include "scrobblemedia.h"
#include <QDebug>
#include <cstdio>
ScrobbleMedia::ScrobbleMedia(QString asUsername, QString asPassword,
QString asArtist, QString asTrack, QString asAlbum)
{
QString KEndPointURL = "http://lastfmstats.livefrombmore.com/universalscrobbler/scrobble.php";
QUrl iScrobbleEndPoint(KEndPointURL);
QNetworkAccessManager *iScrobbleDispatcher = new QNetworkAccessManager(this);
iScrobbleEndPoint.addQueryItem("submissionType","track");
iScrobbleEndPoint.addQueryItem("username", asUsername);
iScrobbleEndPoint.addQueryItem("password", asPassword);
iScrobbleEndPoint.addQueryItem("artist", asArtist);
iScrobbleEndPoint.addQueryItem("track", asTrack);
iScrobbleEndPoint.addQueryItem("album", asAlbum);
iScrobbleEndPoint.addQueryItem("number","1");
iScrobbleEndPoint.addQueryItem("duration","200");
iScrobbleDispatcher->get(QNetworkRequest(iScrobbleEndPoint));
connect(iScrobbleDispatcher, SIGNAL(finished(QNetworkReply*)),
SLOT(replyFinished(QNetworkReply*)));
// QString Outside = iScrobbleEndPoint.toEncoded();
qDebug() << "Received: " +
asUsername + " " +
asPassword + " " +
asArtist + " " +
asTrack + " " +
asAlbum;
qDebug() << iScrobbleEndPoint.toString();
}
ScrobbleMedia::~ScrobbleMedia() {
}
The associated header file looks like:
#ifndef SCROBBLEMEDIA_H
#define SCROBBLEMEDIA_H
#include <QString>
#include <QtNetwork>
#include <QUrl>
#include <QNetworkAccessManager>
class ScrobbleMedia : public QObject
{
Q_OBJECT;
private:
public:
ScrobbleMedia(QString asUsername, QString asPassword, QString asArtist, QString asTrack, QString asAlbum);
~ScrobbleMedia();
};
#endif // SCROBBLEMEDIA_H
I'm currently building the application itself against a MinGW build of version 4.7.0 of the Qt libraries (included as part of Qt SDK 2010.05) under Windows 7 x86-64.
Any assistance would be appreciated.
Thanks in advance.
After reading a number of different sources of information (most of which were contradictory), I've found that the following results in a working solution - albeit with a cosmetic issue in the debugging output that doesn't seem to affect operation (Object::connect: No such signal QNetworkReplyImpl::finished(QNetworkReply*) in ../AudioPlayer/scrobblemedia.cpp:29):
In scrobblemedia.cpp:
#include "scrobblemedia.h"
#include <QDebug>
#include <cstdio>
ScrobbleMedia::ScrobbleMedia(QString asUsername, QString asPassword,
QString asArtist, QString asTrack, QString asAlbum)
{
QByteArray iDataSink;
QEventLoop iLoop;
QString KEndPointURL = "http://lastfmstats.livefrombmore.com/universalscrobbler/scrobble.php";
QUrl iScrobbleEndPoint(KEndPointURL);
iScrobbleEndPoint.addQueryItem("submissionType","track");
iScrobbleEndPoint.addQueryItem("username", asUsername);
iScrobbleEndPoint.addQueryItem("password", asPassword);
iScrobbleEndPoint.addQueryItem("artist", asArtist);
iScrobbleEndPoint.addQueryItem("track", asTrack);
iScrobbleEndPoint.addQueryItem("album", asAlbum);
iScrobbleEndPoint.addQueryItem("number","1");
iScrobbleEndPoint.addQueryItem("duration","200");
QNetworkAccessManager iScrobbleDispatcher;
QNetworkRequest iScrobbleRequest(iScrobbleEndPoint);
QNetworkReply *iScrobbleReply = iScrobbleDispatcher.get(iScrobbleRequest);
QObject::connect(iScrobbleReply, SIGNAL(finished(QNetworkReply*)), &iLoop,
SLOT(quit()));
iDataSink = iScrobbleReply->readAll();
qDebug() << "Received: " + asUsername + " " + asPassword + " " + asArtist + " " + asTrack + " " + asAlbum;
qDebug() << iScrobbleEndPoint.toString();
iLoop.exec();
}
ScrobbleMedia::~ScrobbleMedia() {
}
void ScrobbleMedia::replyFinished(QNetworkReply*) {
}
void ScrobbleMedia::reallyDone() {
qDebug() << "We've probably successfully Scrobbled...";
}
In scrobblemedia.h:
#ifndef SCROBBLEMEDIA_H
#define SCROBBLEMEDIA_H
#include <QString>
#include <QtNetwork>
#include <QUrl>
#include <QNetworkAccessManager>
class ScrobbleMedia : public QObject
{
Q_OBJECT
private:
public:
ScrobbleMedia(QString asUsername, QString asPassword, QString asArtist, QString asTrack, QString asAlbum);
~ScrobbleMedia();
private slots:
void replyFinished(QNetworkReply*);
void reallyDone();
};
#endif // SCROBBLEMEDIA_H
Thanks to everyone for their help.
Hopefully this code will serve as a useful template for others, in the future.