Using QFtp, abort list() command - c++

I am actually wanting to get the newest files from a ftp server. For this, I am currently using QFtp to access the server and retrieve what I need.
This is what I do (like every 3 minutes) :
Connection & authentification to the server.
list() command to list all the files.
for each file listed by the list() command I call a slot that verify if the file currently listed has not been already downloaded (I am relying on the date of the file). If the file is recent enough, I download it.
So, it works. But it it really slow because there are thousands of files on the server and each time I verify the date of each of them. Is it possible to abort the list() command for example when I find a file too old ? Or is there another smarter way to fasten the process ?

Yes, there is a way to abort the long-playing command. When you call QFtp::list() it starts execution command on Ftp server, and if command finds an entry, QFtp emits QFtp::listInfo(const QUrlInfo &) signal. You can handle that signal, and check, whether the QUrlInfo::lastModified() returned time is too old. If yes, you can call QFtp::abort() function to abort the list command's execution on the server. Here is the sample code:
Establish connection to handle the ftp signals
connect(ftp, SIGNAL(listInfo(const QUrlInfo &)),
this, SLOT(onNewEntry(const QUrlInfo &)));
Implementation of the listInfo signal handling slot:
void MyFtp::onNewEntry(const QUrlInfo &url)
{
// If url.lastModified() is less than some time
// ftp->abort();
}

Related

C++ QT best way to pass a message higher in class structure

I am currently implementing a program that realizes TCP communication between PC program and external devices in QT. The problem I have is more general, but I will use this one as an example.
My class hierarchy looks like this:
Program
/ \
Server_A <--> Server_B <--- External system
|
Sockets[]
/ \
Commands Confirmations
/ | \
Interf1 Interf2 Interf3
I can get a command from device (Socket), my command gets into Confirmation class, realizes any Interface job, and returns confirmation back to Socket, which sends it back to device.
The problem occurs when I want to have a command send from an external system, which I also have to confirm.
I get a message on Server_B and pass it to Server_A with information about: socket to send command to and command to realize.
I pass a command to a particular socket
Socket sends a command to Commands, as there is logic for an External System commands.
Commands prepares a message, runs logic, and sends(through socket) message to device
Socket waits for response
Socket gets the response, understands that it was a response to an external system command, and passes it back to Commands
Commands realizes its logic.
Here it would all be fine, but the next step is:
Commands need to confirm the success(or failure) to external system.
So basically, what I have to do is pass a message from Commands to Server_B this way:
Commands->Socket->Server_A->Server_B. For all these classes, I would have to create an unnecessary method just to pass this one information. Is there a way to somehow solve this problem? During my programming, it often occurs that I have to pass something to the higher layer of my class structure, and it looks redundant to realize it through additional methods that only passes information further.
I have provided a sample pseudocode for this problem:
class Program
{
ServerB serverB;
ServerA serverA;
}
class ServerB
{
void send(QString msg);
}
class ServerA
{
QVector<MySocket*> sockets;
}
class MySocket
{
Commands commands;
Confirmations confirmations;
}
class Commands
{
void doLogic();
void sendToExternalSystem(QString message); //How to realize it?
}
My program is much bigger, but I hope it will give you a clue what I am trying to achieve. The simplest solution would be to add a method void sendToExternalSystem(QString message) into Sockets, Server_A and Server_B, aswell as providing a pointer for each parent during construction (commands will have access to sockets, sockets will have access to server_a, and server_a will have access to server_b)
Finally, I came up with a solution. It was necessary to implement ExternalCommand class, which instances were created in Server_B.
In the minimal solution, it has: 1. Field QString Message, 2. Method QString getMessage(), 3. Method void finish(QString), 4. Signal void sendToExternal(QString)
When I read the message sent from the external system in Server_B, I create an instance of this class, and connect it to the Server_B send method. In my code, it looks like that:
ExternalCommand::ExternalCommand(QString message, QObject* parent) : QObject(parent)
{
this->message=message;
}
QString ExternalCommand::getMessage()
{
return this->message;
}
void finish(QString outputMessage)
{
emit sendToExternal(outputMessage);
}
void Server_B::onReadyRead()
{
QTcpSocket *socket = dynamic_cast<QTcpSocket*>(sender());
QString message = socket->readAll();
ExternalCommand* cmd = new ExternalCommand(message);
connect(cmd, &ExternalCommand::sendToExternal, socket,
[socket](QString message) {socket->write(message.toUtf8());});
}
It was also necessary to implement some type of object destruction in ExternalCommand once the command is sent, but it isn't the point of this question.
So once this is implemented, instead of the message as QString, the message is passed to the lower levels as ExternalCommand*, and once an answer is got, it is possible to send it back to the External System, by calling ExternalCommand::finish(QString outputMessage);. Of course, this is just a minimal solution for this problem.
Thanks to #MatG for pointing me to Promise/Future pattern, which was helpful in finding this solution.

Widevine Session Update endless Loop

I am using libwidevinecdm.so from chrome to handle DRM protected data. I am currently successfully setting the widevine server certificate I get from the license server. I can also create a session with the pssh box of the media im trying to decode. So far everything is successful (all promises resolve fine).
(session is created like this: _cdm->CreateSessionAndGenerateRequest(promise_id, cdm::SessionType::kTemporary, cdm::InitDataType::kCenc, pssh_box.data(), static_cast<uint32_t>(pssh_box.size()));)
I am then getting a session message of type kLicenseRequest which I am forwarding to the respective license server. The license server responds with a valid response and the same amount of data as I can see in the browser when using Chrome. I am then passing this to my session like this:
_cdm->UpdateSession(promise_id, session_id.data(), static_cast<uint32_t>(session_id.size()),
license_response.data(), static_cast<uint32_t>(license_response.size()));
The problem now is that this promise never resolves. It keeps posting the kLicenseRequest message over and over again to my session without ever returning. Does this mean my response is wrong? Or is this something else?
Br
Yanick
The issue is caused by the fact, that everything in CreateSessionAndGenerateRequest is done synchronous - that means by the time CreateSessionAndGenerateRequest returns your promise will always be resolved.
The CDM will emit the kLicenseRequest inside CreateSessionAndGenerateRequest and it doesn't do so in a "fire & forget" fashion, but the function waits there until you have returned from the cdm::Host_10::OnSessionMessage. Since my implementation of OnSessionMessage was creating a synchronous HTTP Request to the license server before - also synchronously - calling the UpdateSession the entire chain ended up to be blocking.
So ultimately I was calling UpdateSession while still being inside CreateSessionAndGenerateRequest and I assume the CDM cannot handle this and reacts by creating a new session with the given ID and generating a request again, which of course triggered another UpdateSession and so on.
Ultimately the simplest way to break the cycle was to make something asynchronous. I decided to launch a separate thread when receiving kLicenseRequest, wait for a few milliseconds to make sure that CreateSessionAndGenerateRequest has time to finish (not sure if that is really required) and then issue the request to the license server.
The only change I had to do was adding the surrounding std::thread:
void WidevineSession::forward_license_request(const std::vector<uint8_t> &data) {
std::thread{
[=]() {
std::this_thread::sleep_for(std::chrono::milliseconds{100});
net::HttpRequest request{"POST", _license_server_url};
request.add_header("Authorization", fmt::format("Bearer {}", _access_token))
.byte_body(data);
const auto response = _client.execute(request);
if (response.status_code() != 200) {
log->error("Widevine license request not accepted by license server: {} {} ({})", response.status_code(), response.status_text(), utils::bytes_to_utf8(response.body()));
throw std::runtime_error{"Error requesting widevine license"};
}
log->info("Successfully requested widevine license from license server");
_adapter->update_session(this, _session_id, response.body());
}
}.detach();
}

Qt 4.8.4 how to check if file exists on http server

i need to check the if file exists on http server,
i have the full path and when i try it via browser all works
but when i try in code to do :
if(QFile::exists("http://www.foo.com/hidden/Support/myapp_1.1.2_installer.exe" ))
{
qDebug("file exists");
return true;
}
else
{
qDebug("file not exists");
}
as it writen here :
http://www.qtcentre.org/archive/index.php/t-43712.html?s=b9ae49962c9219aec93b43c514e2ba33
it allways returns me false no matter what ..
what im doing wrong and is it the right way to do this ?
The function QFile::exists is not able to create HTTP requests, which would be necessary to achieve what you are trying to do. The forum discussion you linked to works, because the guy is trying to access a network drive; this is naturally supported by the operating system.
To check whether the file exists, you will have to go the long way around - here is an explanation of how to communicate with a web server: http://developer.nokia.com/Community/Wiki/Creating_an_HTTP_network_request_in_Qt
The Qt class QFile can only deal with files on local filesystem.
You can try out using Qt Network module, probably like this:
QNetworkAccessManager *nam = new QNetworkAccessManager(this);
....
QNetworkRequest req(QUrl("http://www.foo.com/hidden/Support/myapp_1.1.2_installer.exe"));
QNetworkReply *reply = nam->get(req);
connect(reply, SIGNAL(metaDataChanged()),
this, SLOT(slotMetaDataChanged()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
this, SLOT(slotNetworkError(QNetworkReply::NetworkError)));
NOTE THAT if you only want to check for the file's existence, you DON'T want to connect to the finished(QNetworkReply*) signal, because the signal will only be emitted when the network reply has finished processing. That is, the signal will only be emitted after the file is totally downloaded if the file exists.
Then,
slotMetaDataChanged() is called whenever you received new HTTP response headers, you can then check the QNetworkRequest::HttpStatusCodeAttribute for response HTTP codes like 200(OK) or 404(Not Found). In your case, if the returned HTTP code is 200, the file exists.
slotNetworkError() is called when the network request encounters an error, like "Host Not Found" or "Connection Refused", it's up to you to handle these situations in this slot.
The way you are trying to do this, is totally wrong. QFile isn't able to query a webserver. What you need to do is use the QNetworkAccessManager class. With this you can try to download your myapp_1.1.2_installer.exe. If the file does not exist, you will get an error message.
Why the poster on qtcente.org claims it worked for him... no idea. Maybe because his address was a local one. But it still smells fishy.
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
connect(manager, SIGNAL(finished(QNetworkReply*)),
this, SLOT(replyFinished(QNetworkReply*)));
manager->head(QNetworkRequest(QUrl("www.foo.com/hidden/Support/myapp_1.1.2_installer.exe")));
The QNetworkReply in the replyFinished slot has the method NetworkError QNetworkReply::error() const.
You should get a QNetworkReply::ContentNotFoundError if your file does not exist.
Edit: As several comments pointed out, just to learn the existence of a file on a remote server using 'get' and connecting to replyFinished might not be the best of ideas. Might be ok for very small files, but definitely overkill for large blobs of data. I changed the 'get' request into a 'head'. Turner's solution will work, mine should now be an acceptable alternative.

How to Disconnect all connected in QtScript

I use QtScript in my application. Scripts are written by users.
As example script like this:
//<init test time counters>
function testIteration() {
if (<test time is out>) {
myCppObject.mySignalAllDone.disconnect(testIteration);//disconnection
return;
}
//<Adding actions to myCppObject>
}
myCppObject.mySignalAllDone.connect(testIteration);//connection
testIteration();
I want from C++ stop this script before test time passed and write function like this
void MainWindow::toolButtonStopScript_clicked(){
disconnect(&this->myCppObject);// Disconnecting everything connected to myCppObject.
this->scriptEngineThread.abortAllEvaluations();
myCppObject.stopAllActivity();// emits mySignalAllDone, that is not disconnected (why and how to do that if I don't know what connections user made?), calling testIteration(), appending activity to myCppObject and this ends only when test time passed. How to solve this?
this->guiLog.log(GUILog::log_info, tr("Execution of script is interrupted by user"), this->logLevelMsgs);
this->connectMyCppObject();//make default connections
}
How to disconnect properly?
You can disconnect individual signals and slots:
disconnect(sender0, SIGNAL(overflow()),receiver1, SLOT(handleMathError()));
source: http://www.developer.nokia.com/Community/Wiki/Understanding_Signals_and_Slot_in_Qt
To get receivers, use QObject::receivers():
http://qt-project.org/doc/qt-4.8/qobject.html#receivers
It seems you cannot get slots (you must keep track of connections by yourself):
http://qt-project.org/forums/viewthread/6820
However, there are...
...Ways to debug signals and slots:
http://samdutton.wordpress.com/2008/10/03/debugging-signals-and-slots-in-qt/

Qt (QFtp) question

Hello i am learning qt and trying to upload a file using QFtp i wrote the folowing code
this->connect(this->ftp, SIGNAL(done(bool)), this, SLOT(ftpDone(bool)));
this->connect(this->ftp, SIGNAL(dataTransferProgress(qint64, qint64)), this, SLOT(dataTransferProgress(qint64, qint64)));
this->connect(this->ftp, SIGNAL(stateChanged(int)), this, SLOT(stateChanged(int)));
.....
if(this->file.open(QIODevice::ReadWrite))
{
this->ftp->setTransferMode(QFtp::Active);
this->ftp->connectToHost(this->settings->getHost());
this->ftp->login(this->settings->getUser(), this->settings->getPassword());
this->ftp->cd(remoteFilePath);
this->ftp->get(this->fileName, &this->file);
this->ftp->close();
}
and it kind of stops it reports in dataTransferProgress that it is at 0/XXX but the slot is never invoked again (using the same code but with the get function i can download a file and it works without a problem) also the error that i get after the time out is QFtp::UnknownError.
Assuming all the commands until get are successful, it's likely that you are closing the connection before get finishes. You should save the identifier returned by get and call close when the commandFinished signal is called with that identifier.
Note: Except setTransferMode all of the methods you used are asynchronous. They will be executed in the order that they are called, but since you aren't performing any error checking, it's possible for one to fail and the rest will still be attempted which might result in some confusion.
The proper way of doing this is to connectToHost first, if that's successful (you can track this with the commandFinished signal) call login etc.