Logging into a remote website with QT5 - qt

I am attempting to create an application that can login to a website. The specific website is:
http://adfast.biz
Here is the code I am currently using:
void MainWindow::http_finish(QNetworkReply *reply)
{
int code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if (code >= 300 && code < 400)
{ //HTTP 3XX codes are redirections
QUrl redirectTo = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl();
reply->manager()->get(QNetworkRequest(redirectTo));
return;
}
if (reply->error() == QNetworkReply::NoError)
{
QString Msg = QString::fromUtf8(reply->readAll());
if (cdone == 0)
{//Runs only once, causing a reload of the main page
++cdone;
QUrl URL("http://adfast.biz");
QNetworkRequest QNR(URL);
reply->manager()->get(QNR);
QMessageBox::information(0,"1)" + QString::number(code),Msg);
return;
}
QMessageBox::information(0,"2)" + QString::number(code),Msg);
}
else
{
QMessageBox::information(0,"Error:",reply->errorString());
}
reply->deleteLater();
}
void MainWindow::on_Send_clicked()
{
QNetworkAccessManager* MNAM = new QNetworkAccessManager(this); //Stored within QNetworkReply->manager()
connect(MNAM,SIGNAL(finished(QNetworkReply*)),this,SLOT(http_finish(QNetworkReply*)));
QUrlQuery postData;
postData.addQueryItem("email","mail#mail.net");
postData.addQueryItem("senha","Password");
postData.addQueryItem("logar","ok");
QUrl URL(ui->TXT_Input->toPlainText());
URL.setQuery(postData);
QNetworkRequest QNR(URL);
QNR.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
MNAM->post(QNR,URL.toEncoded());
}
I am guessing that I either am sending the information the wrong way or that I need to possibly manage cookies? Both responses come back with HTTP status code: 200. The first comes with no source the second comes with the full web-source but is NOT logged in. I am positive that the user-data being sent is correct, but not that it is being sent correctly.
Edit:
I've changed a little, with no luck. First I have added a cookie-jar using:
QNetworkAccessManager* MNAM = new QNetworkAccessManager(this); //Stored within QNetworkReply->manager()
QNetworkCookieJar* cJar = new QNetworkCookieJar;
MNAM->setCookieJar(cJar);
connect(MNAM,SIGNAL(finished(QNetworkReply*)),this,SLOT(http_finish(QNetworkReply*)));
Then, I tested to see if any cookies are being received using the following code at the top of MainWindow::http_finish:
QList<QNetworkCookie> cookies = reply->manager()->cookieJar()->cookiesForUrl(QUrl("http://adfast.biz/"));
QMessageBox::information(0,"Cookies",QString::number(cookies.count()));
I want to add that the post is being sent to: http://adfast.biz/login (That is the value of: ui->TXT_Input->toPlainText()) But it seems that I can't get this to login at all. And I am not sure what I'm missing.

So, I did manage to login into this site with testguest#yahoo.com and mypassword12. But I can only offer very quick and dirty code, which explains how. The 'polishing' you must do yourself. :-)
QFile f("/tmp/cookie.txt");
f.open(QIODevice::ReadOnly);
QDataStream s(&f);
while(!s.atEnd()){
QByteArray c;
s >> c;
QList<QNetworkCookie> list = QNetworkCookie::parseCookies(c);
qDebug() << "eee" << list;
jar->insertCookie(list.at(0));
}
connect(MNAM,SIGNAL(finished(QNetworkReply*)),
this,SLOT(http_finish(QNetworkReply*)));
QUrlQuery postData;
postData.addQueryItem("email","testguest#yahoo.com");
postData.addQueryItem("senha","mypassword12");
postData.addQueryItem("logar","ok");
//QUrl URL("http://adfast.biz/");
//QUrl URL("http://adfast.biz/anuncios_telexfree");
URL.setQuery(postData);
QNetworkRequest QNR(URL);
QNR.setHeader(QNetworkRequest::ContentTypeHeader,
"application/x-www-form-urlencoded");
MNAM->get(QNR);
Above code reads the cookies, which are written in the http_finish slot below into /tmp/cookie.txt.
The first time you run this code, you must uncomment the first URL, the second time, when you have your cookies, the second URL. Ignore that I made your post into a get. I did it just for debugging reasons.
void MainWindow::http_finish(QNetworkReply *reply){
qDebug() << reply->readAll();
QList<QNetworkCookie> list =
MNAM->cookieJar()->cookiesForUrl(QUrl("http://adfast.biz/"));
QFile f("/tmp/cookie.txt");
f.open(QIODevice::ReadWrite);
for(int i = 0; i < list.size(); ++i){
QDataStream s(&f);
s << list.at(i).toRawForm();
}
f.close();
}
The code above writes the cookies from http://adfast.biz/ into /tmp/cookie.txt. Below an example of a cookie, which I received:
2-Sep-2013 16:41:39 GMT; domain=adfast.biz; path=/3 16:41:39 GMT;
domain=adfast.biz; path=/n=adfast.biz; path=/
Summary: You must connect to http://adfast.biz/ to get your cookie. When you have it, you must again connect, but this time to http://adfast.biz/anuncios_telexfree.

You may need to add a QNetworkCookieJar to your QNetworkAccessManager for keeping and sending cookies between requests and responses. You can set your cookie jar by using QNetworkAccessManager::setCookieJar(QNetworkCookieJar* cookieJar). Have a look at the requests being sent using HttpFox in Firefox and you can see if there are any cookies being sent back and forth.

Try to connect:
connect(&MNAM,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this,SLOT(provideAuthentication(QNetworkReply*,QAuthenticator*)));
And create a slot:
void TrackerClient::provideAuthentication(QNetworkReply *reply,
QAuthenticator *auth){
Q_UNUSED(reply);
auth->setUser(<your username>);
auth->setPassword(<your password>);
}

Ok, second try. The idea to add a cookiejar is correct, but just adding the jar is not enough. I tried the following with another site, which requires login. I did work. For your site I don't have login and password, and since it is not English, it is a little bit too hard for me for a quick help. :-)
How the login procedure works on "my" site.
You go to the main url, e.g. www.mysite.something.
The site asks you for a cookie. You have none.
Your are redirected (status 302 temporary moved) to a
page www.mysite.something/takelogin.php <--- example.
You enter your credentials. You already do this in your postdata.addQuery calls.
You send your post. If your creds are ok, the site sends you a cookie.
So far so good. What did you wrong? Now that you have the cookie you must again go to www.mysite.something. You are not automatically logged in and redirected to the main page of your site. And keep in mind, that cookies in QNetworkCookieJar are not stored on disk. They are only kept in memory. So, if your jar/MNAM is deleted, everything is gone.
You can easily store your received cookies to disk using the toRawForm() method of QNetworkCookie. To restore the cookie use the static QNetworkCooky::parseCookies(const QByteArray &cookieString) method.
Oh, almost forgot: The main url www.mysite.something did not send any cookies. I had to follow the redirect www.mysite.something/takelogin.php.
Disclaimer: It worked on my site. I don't know, if this is general for all sites, which require a login.
Checked with wrong creds: I was redirected to adfast.biz/login. This seems to be your redirect login page. More I cannot do without real creds.

Related

How to intercept request from QWebEngineView and serving them directely from the application?

Is there a way with QWebEngineView to intercept an http request, and to serve it server-less from the app ?
I heard about QWebEngineUrlRequestInterceptor and acceptNavigationRequest(), but they provide only inspection on requests, and redirection for get... But I would like to make the http response from the Qt app.
(I added pyqt in the tags because I would use it from python, but a c++ answer is acceptable too)
To intercept http request you will need to use this code:
// on app startup
QWebEngineProfile.defaultProfile().installUrlSchemeHandler(new
QByteArray("https"), new QWebEngineUrlSchemeHandler() {
#Override public void requestStarted(QWebEngineUrlRequestJob job) {
final String url = job.requestUrl().url();
if (**some url not ok condition**) {
job.fail(QWebEngineUrlRequestJob.Error.UrlInvalid);
}
String data = loadSomeData();
if (data != null) {
QBuffer buffer = new QBuffer();
// this is IMPORTANT! or you will have memory leaks
job.destroyed.connect(buffer::disposeLater);
buffer.open(QIODeviceBase.OpenModeFlag.WriteOnly);
buffer.write(data.getBytes(StandardCharsets.UTF_8));
buffer.close();
job.reply(new QByteArray("text/html"), buffer);
}
}
});
My versions is for QTJambi(Java), but it's not hard to convert it to C++/Python
The qt documentation says the redirection is only for GET request. However, when trying it out (PyQt6==6.4.0) we found out that this is actually not true. If you redirect a POST request in the WebEngine to a local webserver listening on localhost, you will actually receive the request including the payload.
Perhaps this is because Webkit doesn't include the payload for redirect and Chromium does? (I couldn't find docs stating the difference.)
from PyQt6.QtCore import QUrl
from PyQt6.QtWebEngineCore import QWebEngineUrlRequestInterceptor
class WebEngineUrlRequestInterceptor(QWebEngineUrlRequestInterceptor):
def interceptRequest(self, info):
method = info.requestMethod()
if method == "POST":
if info.requestUrl().url() == "https://my-url-to-something":
info.redirect(QUrl("http://127.0.0.1:8000"))

QNetworkReply error "Connection closed" when getting a simple URL

I'm trying to make a simple file downloader. Here is my code segment for downloading a URL; This code works fine, if I try to get simple URLs like "http://stackoverflow.com".
But when I specify URLs which redirects, it throws an error saying "Connection closed".
//Sets a sample URL which redirects.
QString url = "http://downloads.sourceforge.net/project/gretl/gretl/1.9.92/gretl-1.9.92.tar.xz"
QNetworkAccessManager * networkAccessManager = new QNetworkAccessManager(this);
QNetworkRequest * networkRequest = new QNetworkRequest(QUrl(url));
//Getting the URL
QNetworkReply * networkReply = networkAccessManager->get(*networkRequest);
Additionally I've connected the following signal-slot to recognize network errors:
//Connects networkReply object's error signal to a slot which prints the error.
connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(printError(QNetworkReply::NetworkError)));
Here is the slot which prints errors if networkReply's error signal is emitted:
void printError(QNetworkReply::NetworkError code)
{
qDebug() << "Network Error Code: " << code;
qDebug() << networkReply->errorString();
}
Because of this error, networkReply object never receives any header.
My need is to read the HTTP status code by this method: QNetworkReply::attribute(QNetworkRequest::HttpStatusCodeAttribute)
I can't read the HTTP Status code, because I don't get the headers.
Can anyone say whats going on?
Thanks in advance! :)
Here and here are good examples of handling this scenario - basically you add a slot for finished signal, then check the HTTP status code for 301 and/or QNetworkRequest::RedirectionTargetAttribute attribute to see if you are being redirected and if so issue a new request for the redirect link.

Qt code comprehension

i'm struggling with a code snippet for days now, i was wondering if someone could help me understand this code snippet. i'm not asking for code whatsoever, just someone to explain to me this please : (a uri appear to be the complete url to a service)
void RestClient::_prepareRequest( QNetworkRequest& a_request, const QString& a_uri ){
QSslConfiguration config(QSslConfiguration::defaultConfiguration());
config.setProtocol(QSsl::SslV3);
config.setSslOption(QSsl::SslOptionDisableServerNameIndication, true);
a_request.setSslConfiguration(config);
a_request.setRawHeader("Accept", "application/xml");
a_request.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
QByteArray l_api_key; l_api_key.append( toQString( m_api_key) );
QByteArray l_request_hash;
l_request_hash.append( toQString( _buildRequestHash( toStlString(a_uri) ) ) );
a_request.setRawHeader("EMApikey", l_api_key );
a_request.setRawHeader("EMRequestHash", l_request_hash );
a_request.setUrl( QUrl( a_uri ) );
}
So what you've got there is a function taking two parameters, a reference to a QNetworkRequest and a constant reference to a QString for the URI you wish to access. The next lines sets QSslConfiguration to get the default SSL configuration for Qt's network access, and stores it in config. It then sets some further QSsl options and then sets the a_request's SSL settings to be provided by the config you've just set.
Next up it sets some HTTP headers for the request, so these are reasonably standardised, so the Accept references what kind of information is acceptable for the response from the server which in this case is xml (Accept header documentation). The Content-type tells the receiving server what sort of data you're sending in the request body.
The final stage sets a non-standard HTTP header, which is for the application API access key, after that it sets the URL you originally passed and the function is complete. After that the QNetworkRequest can be used with QNetworkAccessManager to send a request to a server, with an API key encoded in, and you'll receive an XML response in return.

How to use post() in qt?

This is my program. In this program I want to send request to a website (for example: http://www.adobe.com/products/muse.html)
I want to show the html code that return me in plain text box.
QUrl url("http://www.adobe.com/products/muse.html")
I want to give html code in "thisfile"
file.setFileName("thisfile.html");
if (!file.open(QIODevice::WriteOnly))
{
std::cerr << "Error: Cannot write file "
<< qPrintable(file.fileName()) << ": "
<< qPrintable(file.errorString()) << std::endl
return false;
}
http.setHost(url.host(),80);
http.post(url.toString(),"term=yyyy&loc=en_us&siteSection=products%3Amuse",&file);
This code doesn't work correctly and when I show the file give me false html code. What do I have to do?
Use http.get() instead of http.post() as POST method requires to set other Headers used by server.
QHttp::get() method is asynchronous too.
As your case is simple enough just to retrieve HTML response, you should go for HTTP GET IMHO. See difference between GET and POST method.
And if you have to use HTTP POST only, then check this.
QNetworkRequest request;
request.setUrl(QUrl("thisfile.html"));
QNetworkReply *reply = manager->post(request, "term=yyyy&loc=en_us&siteSection=products%3Amuse");
connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
Look at QNetworkAccessManager at qt docs
You have to read information and save it to file at readyRead function

Reading HTTP headers

I am trying to connect my application with a web service and here ,a user suggested to send custom headers back to my application.
I am using this code
void Coonnec::serviceRequestFinished(QNetworkReply *reply)
{
QByteArray bytes = reply->readAll();
if (reply->error() != QNetworkReply::NoError) {
qDebug() << "Reply error: " + reply->errorString();
}
else
{
qDebug() << "Uploaded: " + QDateTime::currentDateTime().toString();
qDebug() << reply->rawHeaderList();
}
reply->close();
bytes.clear();
reply->deleteLater();
}
from php i send this header
header('XAppRequest-Status: complete');
When running the application i can see that i get this header but i can't take the value of it cause
reply->rawHeader(bytes);
returns nothing.
How can i take the value 'complete'?
I suggest to connect a slot to the void QNetworkReply::metaDataChanged () signal of your reply.
The Qt doc says
This signal is emitted whenever the metadata in this reply changes.
metadata is any information that is not the content (data) itself,
including the network headers. In the majority of cases, the metadata
will be known fully by the time the first byte of data is received.
However, it is possible to receive updates of headers or other
metadata during the processing of the data.
I do use web-services/client with Qt and I noticed that some header's information are not available when I expected it to be ! I had to 'wait' for this signal to check the header content.

Resources