Qt, catch form values in QNetworkRequest - c++

I have a QWebView component in my Qt widget application. And I have already created a network manager to handle requests from the QWebView.
What I want to do is catching the values of the web form inside QWebView, up to this point of time I have the QNetworkRequest instance of every network request.
How to extract the form values from the QNetworkRequest instance?

You can use this function of QNetworkRequest class:
QByteArray QNetworkRequest::rawHeader ( const QByteArray & headerName ) const
which returns the raw form of header named headerName.

Are you talking about the values on the get method?
int i= 0;
QPair<QString , QString > values;
while( i < ui->webView->url().queryItems().count() )
{
values = ui->webView->url().queryItems().at( i );
i++;
}

Related

HTTP request in C++ for BlackBerry 10

From this webpage, I have code.
When I use "http://httpbin.org/get", everything is OK.
But when I use my own url, for example "http://my-json-server.typicode.com/typicode/demo/db", I'm getting an error:
Unable to retrieve request headers
Where is my fault?
void RequestHeaders::getRequest()
{
//const QUrl url("http://httpbin.org/get"); // OK
const QUrl url("http://my-json-server.typicode.com/typicode/demo/db"); // Not OK
QNetworkRequest request(url);
QNetworkReply* reply = m_networkAccessManager->get(request);
bool ok = connect(reply, SIGNAL(finished()), this, SLOT(onGetReply()));
Q_ASSERT(ok);
Q_UNUSED(ok);
}
void RequestHeaders::onGetReply()
{
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
QString response;
const QByteArray buffer(reply->readAll());
bb::data::JsonDataAccess ja;
const QVariant jsonva = ja.loadFromBuffer(buffer);
const QMap<QString, QVariant> jsonreply = jsonva.toMap();
QMap<QString, QVariant>::const_iterator it = jsonreply.find("headers");
if (it != jsonreply.end()) {
const QMap<QString, QVariant> headers = it.value().toMap();
for (QMap<QString, QVariant>::const_iterator hdrIter = headers.begin();
hdrIter != headers.end(); ++hdrIter) {
if (hdrIter.value().toString().trimmed().isEmpty())
continue;
response += QString::fromLatin1("%1: %2\r\n").arg(hdrIter.key(),
hdrIter.value().toString());
}
}
for (it = jsonreply.begin(); it != jsonreply.end(); it++) {
if (it.value().toString().trimmed().isEmpty())
continue;
response += QString::fromLatin1("%1: %2\r\n").arg(it.key(), it.value().toString());
}
reply->deleteLater();
if (response.trimmed().isEmpty()) {
response = tr("Unable to retrieve request headers");
}
emit complete(response);
}
Your onGetReply handler is parsing the server's HTTP response body as JSON, searching it for a "headers" child field, and if found then extracts that child's own child fields into a local response variable.
http://httpbin.org/get responds with a JSON object containing a "headers" child object that has child fields in it. So your response variable ends up not being empty.
http://my-json-server.typicode.com/typicode/demo/db responds with a JSON object that does not contain any "headers" child. So your response variable is left empty.
You need to either:
fix your server to respond with JSON that actually matches what your code is expecting.
fix your onGetReply() code to handle the JSON that your server is actually sending.

How to add a token when I use the GET method in Qt?

I would like to add a token in my GET request in C++/Qt.
This is my GET / download method :
QNetworkReply* DownloadManager::doDownload(const QUrl &url)
{
QNetworkRequest request(url);
QNetworkReply *reply = m_manager.get(request); // m_manager is a QNetworkAcessManager
return reply;
}
The tokens are sent as part of the request header, so for that case we use the setRawHeader() method:
void QNetworkRequest::setRawHeader(const QByteArray &headerName, const
QByteArray &headerValue)
Sets the header headerName to be of value headerValue. If headerName
corresponds to a known header (see QNetworkRequest::KnownHeaders), the
raw format will be parsed and the corresponding "cooked" header will
be set as well.
In the case of the token we use the following:
request.setRawHeader(QByteArray("Authorization"), QByteArray("Token your_token"));

How to set redirect_uri using QOAuth2AuthorizationCodeFlow and QOAuthHttpServerReplyHandler

For OAuth 2.0 using QT's networkauth and the new QOAuth2AuthorizationCodeFlow object, how can I set the redirect_uri? My code is below. It results in the following authenticate url being sent:
QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl: https://accounts.google.com/o/oauth2/auth?client_id=123-abc.apps.googleusercontent.com&redirect_uri=http://localhost:65535/cb&response_type=code&scope=email&state=iEIYn5sN
The setting of redirect_uri to "http://localhost", results in an Error 400 redirect_uri_mismatch from google which is obviously expecting the actual redirect hostname to be provided.
GoogleGateway::GoogleGateway() {
auto google = new QOAuth2AuthorizationCodeFlow;
google->setScope("email");
this->connect(google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);
QString val;
QFile file;
file.setFileName("/home/me/client_secret.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();
}
To set the redirect_uri, I've tried replacing:
auto replyHandler = new QOAuthHttpServerReplyHandler(port, this);
with
QHostAddress hostaddress = QHostAddress(quint32(1233...));
auto replyHandler = new QOAuthHttpServerReplyHandler(hostaddress, port, this);
with no change in the result.
Have also tried inserting:
replyHandler->setProperty("redirect_uri", "http://abc.xyz.com:65535/cb");
also with no change in the result.
In Qt/5.8/Src/qtnetworkauth/src/oauth/qoauthhttpserverreplyhandler.cpp, we see that the callback address looks suspiciously hard-coded:
QString QOAuthHttpServerReplyHandler::callback() const
{
Q_D(const QOAuthHttpServerReplyHandler);
Q_ASSERT(d->httpServer.isListening());
const QUrl url(QString::fromLatin1("http://localhost:%1/cb").arg(d->httpServer.serverPort()));
return url.toString(QUrl::EncodeDelimiters);
}
This callback() is in turn used in Qt/5.8/Src/qtnetworkauth/src/oauth/qoauth2authorizationcodeflow.cpp to set the redirectUri value:
QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QVariantMap &parameters)
{
Q_D(QOAuth2AuthorizationCodeFlow);
using Key = QAbstractOAuth2Private::OAuth2KeyString;
if (d->state.isEmpty())
setState(QAbstractOAuth2Private::generateRandomState());
Q_ASSERT(!d->state.isEmpty());
const QString state = d->state;
QVariantMap p(parameters);
QUrl url(d->authorizationUrl);
p.insert(Key::responseType, responseType());
p.insert(Key::clientIdentifier, d->clientCredentials.first);
p.insert(Key::redirectUri, callback());
p.insert(Key::scope, d->scope);
p.insert(Key::state, state);
if (d->modifyParametersFunction)
d->modifyParametersFunction(Stage::RequestingAuthorization, &p);
url.setQuery(d->createQuery(p));
connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::callbackReceived, this,
&QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived, Qt::UniqueConnection);
setStatus(QAbstractOAuth::Status::NotAuthenticated);
qDebug("QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl: %s", qPrintable(url.toString()));
return url;
}
Is this a bug?
I just solved this by subclassing MyOAuthHttpServerReplyHandler and overriding the definition of callback() to return the URI I wanted.
I'm using Qt 5.15, the redirect_uri could be change form QAbstractOAuth::modifyParametersFunction
m_google = new QOAuth2AuthorizationCodeFlow(m_manager,this);
// set other parameters...
m_google->setModifyParametersFunction(buildModifyParametersFunction());
// return ModifyParametersFunction()
QAbstractOAuth::ModifyParametersFunction GoogleOAuth2Wrapper::buildModifyParametersFunction()
{
const QUrl clientIdentifier = m_google->clientIdentifier();
const QUrl clientIdentifierSharedKey = m_google->clientIdentifierSharedKey();
return [clientIdentifier,clientIdentifierSharedKey]
(QAbstractOAuth::Stage stage, QVariantMap *parameters){
if(stage == QAbstractOAuth::Stage::RequestingAuthorization){
parameters->insert("redirect_uri","https://127.0.0.1:8080/cb"); /*change redirect uri*/
}
};
}
QOAuth2AuthorizationCodeFlow class use QUrl buildAuthenticateUrl(const QVariantMap &parameters) method to send browser for access token,here's source:
QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QVariantMap &parameters)
{
Q_D(QOAuth2AuthorizationCodeFlow);
using Key = QAbstractOAuth2Private::OAuth2KeyString;
if (d->state.isEmpty())
setState(QAbstractOAuth2Private::generateRandomState());
Q_ASSERT(!d->state.isEmpty());
const QString state = d->state;
QVariantMap p(parameters);
QUrl url(d->authorizationUrl);
p.insert(Key::responseType, responseType());
p.insert(Key::clientIdentifier, d->clientIdentifier);
p.insert(Key::redirectUri, callback());
p.insert(Key::scope, d->scope);
p.insert(Key::state, state);
if (d->modifyParametersFunction) /** Here's what we take part **/
d->modifyParametersFunction(Stage::RequestingAuthorization, &p);
url.setQuery(d->createQuery(p));
connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::callbackReceived, this,
&QOAuth2AuthorizationCodeFlow::authorizationCallbackReceived, Qt::UniqueConnection);
setStatus(QAbstractOAuth::Status::NotAuthenticated);
qCDebug(d->loggingCategory, "Generated URL: %s", qPrintable(url.toString()));
return url;
}
I hope that is helpful.
Reference
QAbstractOAuth::modifyParametersFunction
QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl source
Using Qt 5.15, having your redirect URI overridden is no longer a problem, but there are two others that you might be running into:
If you use the first URL provided in Google's credential JSON, your app might not hearing back from the user's browser at the end of authorization flow. Instead, just use http://127.0.0.1:1234/ which is reliably the user's machine.
Depending on your luck, the login code returned by Google is URL-encoded and it may contain characters that need to be URL-decoded before requesting a login token from Google itself. This is something that I'd expect Qt to take care of for you, but instead you have to inject a parameter modifier.
Here is the key block, taken from our post on authenticating a Qt app with Google SSO:
this->google->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
// Percent-decode the "code" parameter so Google can match it
if (stage == QAbstractOAuth::Stage::RequestingAccessToken) {
QByteArray code = parameters->value("code").toByteArray();
(*parameters)["code"] = QUrl::fromPercentEncoding(code);
}
});

Qt pass additional argument to slot AND keep emitted signal data

I have searched the web on this issue and I've repeatedly got answers referring to the use of QSignalMapper. But my problem is pretty clear, QSignalMapper automatically gets rid of whatever is originally emitted and replaces it with basically nothing, plus the new data that is set via setMapping().
The problem here is simple.
I have a QNetworkAccessManager that parses html and updates a vector containing text data:
void DataManager::startHttpRequest(QString url, int index)
{
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
//QSignalMapper* signalMapper = new QSignalMapper(this);
//connect(manager,SIGNAL(finished(QNetworkReply*)), signalMapper,SLOT(map()));
//signalMapper->setMapping(manager, index);
//connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(insertUpdate(int)));
connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(finishHttpRequest(QNetworkReply*)));
manager->get(QNetworkRequest(QUrl(url)));
qDebug() << index;
}
and here is what happens when the request is finished, the normal way:
void DataManager::finishHttpRequest(QNetworkReply *reply)
{
QString html = QString(reply->readAll()).simplified();
QString info;
int start = html.indexOf("<span id=\"SalePrice\" >");
if(start != -1)
{
QString price = html.mid(start + 23, 30);
int end = price.indexOf("</span>");
info = price.mid(0, end - 1);
qWarning() << price.mid(0, end - 1);
}
else
{
info = "NA";
}
// Do more stuff
}
Using the normal way of signals and slots, I would not be able to know the index of the vector I am updating,
Or,
If I am using QSignalMapper, I know the index, but not the data that comes with it.
How do I get BOTH working (index + data)?
(something like mySlot(QNetworkReply *reply, int *index), but we all know that won't work)
Many thanks in advance.
While it's probably not the best,
sender()->setObjectName(const QString & name) allows the sender to name itself.
The sender's name can be accessed from the receiving slot via sender()->ObjectName()
As documented on http://qt-project.org/doc/qt-5/qobject.html#objectName-prop.

How to use Google Translate API With c++

I'm a beginner in C++ and using Google task API.
How do I write a C++ program that accepts a word, then invokes Google Translate to translate it from English to French, then saves the resulted page to a local file?
For example, if the user inputs "river", the program should invoke Google Translate to translate into French, the resulting page is:
http://translate.google.com/#en|fr|River%0A
This page should be saved.
I read the official documentation through fully: http://code.google.com/apis/language/translate/v2/getting_started.html
but I couldn't understand how to using REST and I'm not familiar with JSON or AJAX.
You cannot use JSON objects straight in C++.
JSON = JavaScript Object Notation
You need to spawn/create something which can parse such objects. For example I've used QNetworkManager in Qt (in C++) to send build javascript and send requests to google APIs. The result would be a JSON object which I had to parse and either display in a browser (which I made in c++) or parse the results into a c++ class and process it differently (do some calculations with it).
If you just need the data, you can request XML instead of JSON and then you can use a standard XML parser to extract the translated word.
EDIT 1:
Google in their example uses:
https://www.googleapis.com/language/translate/v2?key=YOUR-API-KEY&source=en&target=de&q=words+to+translate
This translate from english (en) to german (de). The words to translate are: "words to translate".
Now build an HTTP request in C++ with this URL as the data. Send that with some network manager or sockets and wait for a reply back. Google will give you back data.
I seen this codes below somewhere but I don't remember where, anyway try this:
QString YourClass::translate(QString keyword, QString from, QString to)
{
//Translate URL
QString url = QString("http://translate.google.com/translate_a/t?client=t&text=%0&hl=%1&sl=%2&tl=%1&multires=1&prev=enter&oc=2&ssel=0&tsel=0&uptl=%1&sc=1").arg(keyword).arg(to).arg(from);
QNetworkAccessManager manager;
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
//Get reply from Google
do {
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
} while(!reply->isFinished());
//Convert to string
QString translation(reply->readAll());
reply->close();
//Free memory
delete reply;
//Remove [[[" from the beginning
translation = translation.replace("[[[\"", "");
//Extract final translated string
translation = translation.mid(0, translation.indexOf(",\"") - 1);
return translation;
}
someone advise me to use libcurl, I will try and see.
You need some kind of HTTP library. I second most of what Adrian said, but here's some (incomplete) Qt code which should give you an idea.
namespace {
const QString API_KEY = /* TODO */;
}
MyQObject :: MyQbject ()
: m_manager (new QNetworkAccessManager (this))
{
connect(manager, SIGNAL (finished (QNetworkReply *)),
this, SLOT (reply_finished (QNetworkReply *)));
}
void MyQObject :: fetch_translation (
QString words,
void (*on_receive)(const QString &))
{
// Let's assume asynchronous but non-concurrent requests.
m_on_receive = on_receive;
QString request =
"https://www.googleapis.com/language/translate/v2"
"?key=%1&source=en&target=de&q=%2";
// May want to url-encode 'words' first.
m_manager -> get (QUrl (request .arg (API_KEY) .arg (words));
}
void MyQObject :: reply_finished (QNetworkReply * reply)
{
m_on_receive (reply -> readAll ());
}
// ...
{
my_q_object -> translate ("hello, world", [](const QString & s) {
qWarning () << "translation is: " << s;
});
}