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.
Related
I have created a small program using QT creator 4.14.0 (QT 5.15.2), using c++ and QML. I am creating, building and running the program on Ubuntu using Virtual box on a windows 10 system. I have installed bluez and other bluetooth packages on the virtual Ubuntu system as per instructions I've found online, and I have bluetooth working in the Ubuntu operating system, I can scan for devices using the Bluetooth quick-tray item and it finds what it's supposed to find, the bluetooth adapter works.
However, when I run my program I get the the following under Application output "qt.bluetooth: Dummy backend running. Qt Bluetooth module is non-functional.", and the bluetooth scan never starts (no further output). From what I can gather by research it seems like QT thinks that the system can not support Bluetooth, and uses a "dummy stub" in lieu of the actual working code. I have researched what I can about how to resolve this, but I can not find anything I can apply to my own code/setup. I would be appreciative of any help I can get.
Here's the relevant code:
// this is bluetooth_scanner.h
#ifndef BLUETOOTH_SCANNER_H
#define BLUETOOTH_SCANNER_H
#include <QObject>
#include <QBluetoothDeviceDiscoveryAgent>
#include <QBluetoothDeviceInfo>
#include <QDir>
#include <QTextStream>
class bluetooth_scanner : public QObject
{
Q_OBJECT
public:
bluetooth_scanner();
void startDeviceDiscovery();
public slots:
void deviceDiscovered(const QBluetoothDeviceInfo &device);
};
#endif // BLUETOOTH_SCANNER_H
// this is bluetooth_scanner.cpp
#include "bluetooth_scanner.h"
// constructor
bluetooth_scanner::bluetooth_scanner()
{
}
void bluetooth_scanner::startDeviceDiscovery()
{
// Create a discovery agent and connect to its signals
QBluetoothDeviceDiscoveryAgent *discoveryAgent = new QBluetoothDeviceDiscoveryAgent(); // was 'this' inside parantheses, got build error, removed == no build error
QObject::connect(discoveryAgent, SIGNAL(deviceDiscovered(QBluetoothDeviceInfo)),this, SLOT(deviceDiscovered(QBluetoothDeviceInfo)));
// Start a discovery
discoveryAgent->start();
}
// In your local slot, read information about the found devices
void bluetooth_scanner::deviceDiscovered(const QBluetoothDeviceInfo &device)
{
// init QT printing
QTextStream out(stdout);
out << "Found new device:" << device.name() << '(' << device.address().toString() << ')' << endl;
}
// this is main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <bluetooth_scanner.h>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
// test bluetooth C++ scan function
bluetooth_scanner *bt_scan = new bluetooth_scanner();
bt_scan->startDeviceDiscovery();
return app.exec();
}
For some application I need to scan WiFi searching for available network (SSID). On Ubuntu 18.04 LTS I don't have any issue with the fallowing code. It return all available SSID as expected but when running on Raspbian stretch (Rasberry PI 3 b) the fallowing code is returning the interface wlan0 and eth0 and not the SSID. Does someone have any clue why? or can tell me what I miss? On the other hand, is it better not to use Qt network lib and use a QProcess and parse what sudo iwlist wlan0 scan is returning?
.cpp
void wificonfig::findavailableWifinetwork( void )
{
QNetworkConfigurationManager ncm;
netconflist = ncm.allConfigurations(QNetworkConfiguration::Discovered); //
int i =0;
awifilist.clear();
for(auto &x : netconflist){
if ((x.bearerType() == QNetworkConfiguration::BearerWLAN) ){
if(x.name() == "")
awifilist << "Unknown(Other Network)";
else{
ui->wificonnection_tw->setItem(i, 0, new QTableWidgetItem(x.name()));
i++;
}
//qDebug() << x.type();
}
}
ui->wificonnection_tw->setRowCount(i);
}
.h
#ifndef WIFICONFIG_H
#define WIFICONFIG_H
#include <QList>
#include <QTimer>
#include <QDialog>
#include <QNetworkConfiguration>
#include <QNetworkConfigurationManager>
#include <QNetworkSession>
namespace Ui {
class wificonfig;
}
class wificonfig : public QDialog
{
Q_OBJECT
public:
explicit wificonfig(QWidget *parent = 0);
~wificonfig();
int connection_count;
QNetworkConfiguration netconf;
QStringList awifilist;
QList<QNetworkConfiguration>netconflist;
public slots:
void findavailableWifinetwork( void );
private slots:
void on_wifi_scan_butt_clicked();
private:
Ui::wificonfig *ui;
QTime *scan_timeout_timer;
QNetworkSession *session;
};
#endif // WIFICONFIG_H
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();
});
});
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
Clean install of Qt SDK 1.1.4 on Windows 7 with Visual C++ 2008 SP1; I'm using Qt Creator. Why does this code not load some web pages?
#include <QtGui/QApplication>
#include <QtWebKit/QWebView>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWebView b;
b.load(QUrl("https://gmail.com")); // doesn't work
//b.load(QUrl("https://accounts.google.com")); // works
//b.load(QUrl("https://google.com")); // doesn't work
//b.load(QUrl("https://www.google.com")); // works
b.show();
return a.exec();
}
Why do some of the URLs not work, and others do?
I think the google.com / www.google.com is especially telling; google.com normally redirects to www.google.com. And gmail.com is redirecting to accounts.google.com. Is WebKit not allowing secure pages to redirect? If so, how to fix that?
By the way, Qt SDK 1.1.4 seems to include OpenSSL; I noticed its presence at C:\QtSDK\Desktop\Qt\4.7.4\msvc2008\bin\ssleay32.dll. Also notice that some pages seem to work, just not others.
EDIT: Two more URLs:
b.load(QUrl("https://support.motionview3d.com/help/_media/images/directory.png")); // doesn't work
b.load(QUrl("https://mail.google.com")); // works
Again, both of these work fine in other web browsers.
You are probably getting SSL errors which you can handle in a slot. Although not the best final solution, you can use the slot to ignore all SSL errors. I did this by subclassing QWebView:
qwebview.h:
#ifndef WEBVIEW_H
#define WEBVIEW_H
#include <QWebView>
class WebView : public QWebView
{
Q_OBJECT
public:
WebView(QWidget *parent = 0);
private slots:
void handleSslErrors(QNetworkReply* reply, const QList<QSslError> &errors);
};
#endif // WEBVIEW_H
qwebview.cpp:
#include "webview.h"
#include <QNetworkReply>
#include <QtDebug>
#include <QSslError>
WebView::WebView(QWidget *parent) :
QWebView(parent)
{
load(QUrl("https://gmail.com"));
connect(page()->networkAccessManager(),
SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError> & )),
this,
SLOT(handleSslErrors(QNetworkReply*, const QList<QSslError> & )));
}
void WebView::handleSslErrors(QNetworkReply* reply, const QList<QSslError> &errors)
{
qDebug() << "handleSslErrors: ";
foreach (QSslError e, errors)
{
qDebug() << "ssl error: " << e;
}
reply->ignoreSslErrors();
}
main.cpp"
#include <QApplication>
#include "WebView.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
WebView w;
w.show();
return a.exec();
}
Running this should produce debug output like this:
handleSslErrors:
ssl error: "The host name did not match any of the valid hosts for this certificate"
ssl error: "No error"
ssl error: "No error"
...
In your final program, you will of course want to handle SSL errors properly :)
I usually use the "Arnold Spence"s solution but sometimes that wont work.
in that case just change the default Ssl configuration like this
QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration();
QList<QSslCertificate> cert_list = sslconf.caCertificates();
QList<QSslCertificate> cert_new = QSslCertificate::fromData("CaCertificates");
cert_list += cert_new;
sslconf.setCaCertificates(cert_list);
sslconf.setProtocol(QSsl::AnyProtocol);
QSslConfiguration::setDefaultConfiguration(sslconf);
Here we altered the configuration for the entire application .
I recommend you handle the sslErrors signal too ..