From 2089d6af867e9721ab6e9712cbe5c2e6363a4cf0 Mon Sep 17 00:00:00 2001 From: blue Date: Wed, 18 Sep 2019 16:27:47 +0300 Subject: [PATCH] download files error handling --- core/networkaccess.cpp | 137 ++++++++++++++++++++++++++++++++++-- core/networkaccess.h | 3 +- core/squawk.cpp | 1 + core/squawk.h | 1 + main.cpp | 1 + ui/squawk.cpp | 24 ++++++- ui/squawk.h | 1 + ui/widgets/conversation.cpp | 5 ++ ui/widgets/conversation.h | 1 + ui/widgets/message.cpp | 19 ++++- ui/widgets/message.h | 3 + ui/widgets/messageline.cpp | 11 +++ ui/widgets/messageline.h | 1 + 13 files changed, 199 insertions(+), 9 deletions(-) diff --git a/core/networkaccess.cpp b/core/networkaccess.cpp index 7fd3b06..002f9d7 100644 --- a/core/networkaccess.cpp +++ b/core/networkaccess.cpp @@ -21,7 +21,7 @@ Core::NetworkAccess::NetworkAccess(QObject* parent): QObject(parent), running(false), - manager(), + manager(0), files("files"), downloads() { @@ -29,7 +29,7 @@ Core::NetworkAccess::NetworkAccess(QObject* parent): Core::NetworkAccess::~NetworkAccess() { - + stop(); } void Core::NetworkAccess::fileLocalPathRequest(const QString& messageId, const QString& url) @@ -93,6 +93,7 @@ void Core::NetworkAccess::downladFileRequest(const QString& messageId, const QSt void Core::NetworkAccess::start() { if (!running) { + manager = new QNetworkAccessManager(); files.open(); running = true; } @@ -102,7 +103,14 @@ void Core::NetworkAccess::stop() { if (running) { files.close(); + manager->deleteLater(); + manager = 0; running = false; + + for (std::map::const_iterator itr = downloads.begin(), end = downloads.end(); itr != end; ++itr) { + itr->second->success = false; + itr->second->reply->abort(); //assuming it's gonna call onRequestFinished slot + } } } @@ -133,7 +141,128 @@ void Core::NetworkAccess::onRequestError(QNetworkReply::NetworkError code) if (itr == downloads.end()) { qDebug() << "an error downloading" << url << ": the request is reporting an error but seems like noone is waiting for it, skipping"; } else { - itr->second->success = false; + QString errorText; + switch (code) { + case QNetworkReply::NoError: + //this never is supposed to happen + break; + + // network layer errors [relating to the destination server] (1-99): + case QNetworkReply::ConnectionRefusedError: + errorText = "Connection refused"; + break; + case QNetworkReply::RemoteHostClosedError: + errorText = "Remote server closed the connection"; + break; + case QNetworkReply::HostNotFoundError: + errorText = "Remote host is not found"; + break; + case QNetworkReply::TimeoutError: + errorText = "Connection was closed because it timed out"; + break; + case QNetworkReply::OperationCanceledError: + //this means I closed it myself by abort() or close(), don't think I need to notify here + break; + case QNetworkReply::SslHandshakeFailedError: + errorText = "Security error"; //TODO need to handle sslErrors signal to get a better description here + break; + case QNetworkReply::TemporaryNetworkFailureError: + //this means the connection is lost by opened route, but it's going to be resumed, not sure I need to notify + break; + case QNetworkReply::NetworkSessionFailedError: + errorText = "Outgoing connection problem"; + break; + case QNetworkReply::BackgroundRequestNotAllowedError: + errorText = "Background request is not allowed"; + break; + case QNetworkReply::TooManyRedirectsError: + errorText = "The request was redirected too many times"; + break; + case QNetworkReply::InsecureRedirectError: + errorText = "The request was redirected to insecure connection"; + break; + case QNetworkReply::UnknownNetworkError: + errorText = "Unknown network error"; + break; + + // proxy errors (101-199): + case QNetworkReply::ProxyConnectionRefusedError: + errorText = "The connection to the proxy server was refused"; + break; + case QNetworkReply::ProxyConnectionClosedError: + errorText = "Proxy server closed the connection"; + break; + case QNetworkReply::ProxyNotFoundError: + errorText = "Proxy host was not found"; + break; + case QNetworkReply::ProxyTimeoutError: + errorText = "Connection to the proxy server was closed because it timed out"; + break; + case QNetworkReply::ProxyAuthenticationRequiredError: + errorText = "Couldn't connect to proxy server, authentication is required"; + break; + case QNetworkReply::UnknownProxyError: + errorText = "Unknown proxy error"; + break; + + // content errors (201-299): + case QNetworkReply::ContentAccessDenied: + errorText = "The access to file is denied"; + break; + case QNetworkReply::ContentOperationNotPermittedError: + errorText = "The operation over requesting file is not permitted"; + break; + case QNetworkReply::ContentNotFoundError: + errorText = "The file was not found"; + break; + case QNetworkReply::AuthenticationRequiredError: + errorText = "Couldn't access the file, authentication is required"; + break; + case QNetworkReply::ContentReSendError: + errorText = "Sending error, one more attempt will probably solve this problem"; + break; + case QNetworkReply::ContentConflictError: + errorText = "The request could not be completed due to a conflict with the current state of the resource"; + break; + case QNetworkReply::ContentGoneError: + errorText = "The requested resource is no longer available at the server"; + break; + case QNetworkReply::UnknownContentError: + errorText = "Unknown content error"; + break; + + // protocol errors + case QNetworkReply::ProtocolUnknownError: + errorText = "Unknown protocol error"; + break; + case QNetworkReply::ProtocolInvalidOperationError: + errorText = "Requested operation is not permitted in this protocol"; + break; + case QNetworkReply::ProtocolFailure: + errorText = "Low level protocol error"; + break; + + // Server side errors (401-499) + case QNetworkReply::InternalServerError: + errorText = "Internal server error"; + break; + case QNetworkReply::OperationNotImplementedError: + errorText = "Server doesn't support requested operation"; + break; + case QNetworkReply::ServiceUnavailableError: + errorText = "The server is not available for this operation right now"; + break; + case QNetworkReply::UnknownServerError: + errorText = "Unknown server error"; + break; + } + if (errorText.size() > 0) { + itr->second->success = false; + Download* dwn = itr->second; + for (std::set::const_iterator mItr = dwn->messages.begin(), end = dwn->messages.end(); mItr != end; ++mItr) { + emit downloadFileError(*mItr, errorText); + } + } } } @@ -193,7 +322,7 @@ void Core::NetworkAccess::startDownload(const QString& messageId, const QString& { Download* dwn = new Download({{messageId}, 0, 0, true}); QNetworkRequest req(url); - dwn->reply = manager.get(req); + dwn->reply = manager->get(req); connect(dwn->reply, SIGNAL(downloadProgress(qint64, qint64)), SLOT(onDownloadProgress(qint64, qint64))); connect(dwn->reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onRequestError(QNetworkReply::NetworkError))); connect(dwn->reply, SIGNAL(finished()), SLOT(onRequestFinished())); diff --git a/core/networkaccess.h b/core/networkaccess.h index 1d44dbe..526cf2d 100644 --- a/core/networkaccess.h +++ b/core/networkaccess.h @@ -50,6 +50,7 @@ public: signals: void fileLocalPathResponse(const QString& messageId, const QString& path); void downloadFileProgress(const QString& messageId, qreal value); + void downloadFileError(const QString& messageId, const QString& path); public slots: void fileLocalPathRequest(const QString& messageId, const QString& url); @@ -65,7 +66,7 @@ private slots: private: bool running; - QNetworkAccessManager manager; + QNetworkAccessManager* manager; Storage files; std::map downloads; diff --git a/core/squawk.cpp b/core/squawk.cpp index 0468daa..bf9550f 100644 --- a/core/squawk.cpp +++ b/core/squawk.cpp @@ -30,6 +30,7 @@ Core::Squawk::Squawk(QObject* parent): { connect(&network, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), this, SIGNAL(fileLocalPathResponse(const QString&, const QString&))); connect(&network, SIGNAL(downloadFileProgress(const QString&, qreal)), this, SIGNAL(downloadFileProgress(const QString&, qreal))); + connect(&network, SIGNAL(downloadFileError(const QString&, const QString&)), this, SIGNAL(downloadFileError(const QString&, const QString&))); } Core::Squawk::~Squawk() diff --git a/core/squawk.h b/core/squawk.h index cdcd474..89f4c2d 100644 --- a/core/squawk.h +++ b/core/squawk.h @@ -63,6 +63,7 @@ signals: void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); void fileLocalPathResponse(const QString& messageId, const QString& path); + void downloadFileError(const QString& messageId, const QString& error); void downloadFileProgress(const QString& messageId, qreal value); public slots: diff --git a/main.cpp b/main.cpp index e4a2618..d0fba26 100644 --- a/main.cpp +++ b/main.cpp @@ -123,6 +123,7 @@ int main(int argc, char *argv[]) &w, SLOT(removeRoomParticipant(const QString&, const QString&, const QString&))); QObject::connect(squawk, SIGNAL(fileLocalPathResponse(const QString&, const QString&)), &w, SLOT(fileLocalPathResponse(const QString&, const QString&))); QObject::connect(squawk, SIGNAL(downloadFileProgress(const QString&, qreal)), &w, SLOT(downloadFileProgress(const QString&, qreal))); + QObject::connect(squawk, SIGNAL(downloadFileError(const QString&, const QString&)), &w, SLOT(downloadFileError(const QString&, const QString&))); //qDebug() << QStandardPaths::writableLocation(QStandardPaths::CacheLocation); diff --git a/ui/squawk.cpp b/ui/squawk.cpp index 27f279c..f30a8bb 100644 --- a/ui/squawk.cpp +++ b/ui/squawk.cpp @@ -369,6 +369,25 @@ void Squawk::downloadFileProgress(const QString& messageId, qreal value) } } +void Squawk::downloadFileError(const QString& messageId, const QString& error) +{ + std::map>::const_iterator itr = requestedFiles.find(messageId); + if (itr == requestedFiles.end()) { + qDebug() << "downloadFileError in UI Squawk but there is nobody waiting for that id" << messageId << ", skipping"; + return; + } else { + const std::set& convs = itr->second; + for (std::set::const_iterator cItr = convs.begin(), cEnd = convs.end(); cItr != cEnd; ++cItr) { + const Models::Roster::ElId& id = *cItr; + Conversations::const_iterator c = conversations.find(id); + if (c != conversations.end()) { + c->second->downloadError(messageId, error); + } + } + requestedFiles.erase(itr); + } +} + void Squawk::fileLocalPathResponse(const QString& messageId, const QString& path) { std::map>::const_iterator itr = requestedFiles.find(messageId); @@ -376,8 +395,7 @@ void Squawk::fileLocalPathResponse(const QString& messageId, const QString& path qDebug() << "fileLocalPathResponse in UI Squawk but there is nobody waiting for that path, skipping"; return; } else { - std::set convs = itr->second; - requestedFiles.erase(itr); + const std::set& convs = itr->second; for (std::set::const_iterator cItr = convs.begin(), cEnd = convs.end(); cItr != cEnd; ++cItr) { const Models::Roster::ElId& id = *cItr; Conversations::const_iterator c = conversations.find(id); @@ -385,6 +403,8 @@ void Squawk::fileLocalPathResponse(const QString& messageId, const QString& path c->second->responseLocalFile(messageId, path); } } + + requestedFiles.erase(itr); } } diff --git a/ui/squawk.h b/ui/squawk.h index 5d5d2c6..99a6be7 100644 --- a/ui/squawk.h +++ b/ui/squawk.h @@ -91,6 +91,7 @@ public slots: void changeRoomParticipant(const QString& account, const QString& jid, const QString& name, const QMap& data); void removeRoomParticipant(const QString& account, const QString& jid, const QString& name); void fileLocalPathResponse(const QString& messageId, const QString& path); + void downloadFileError(const QString& messageId, const QString& error); void downloadFileProgress(const QString& messageId, qreal value); private: diff --git a/ui/widgets/conversation.cpp b/ui/widgets/conversation.cpp index 73fc851..9210d9e 100644 --- a/ui/widgets/conversation.cpp +++ b/ui/widgets/conversation.cpp @@ -290,6 +290,11 @@ void Conversation::responseDownloadProgress(const QString& messageId, qreal prog line->responseDownloadProgress(messageId, progress); } +void Conversation::downloadError(const QString& messageId, const QString& error) +{ + line->downloadError(messageId, error); +} + void Conversation::responseLocalFile(const QString& messageId, const QString& path) { line->responseLocalFile(messageId, path); diff --git a/ui/widgets/conversation.h b/ui/widgets/conversation.h index 3d1529c..0a2fa41 100644 --- a/ui/widgets/conversation.h +++ b/ui/widgets/conversation.h @@ -72,6 +72,7 @@ public: void responseArchive(const std::list list); void showEvent(QShowEvent * event) override; void responseLocalFile(const QString& messageId, const QString& path); + void downloadError(const QString& messageId, const QString& error); void responseDownloadProgress(const QString& messageId, qreal progress); signals: diff --git a/ui/widgets/message.cpp b/ui/widgets/message.cpp index 54cbcfc..e5d4852 100644 --- a/ui/widgets/message.cpp +++ b/ui/widgets/message.cpp @@ -38,10 +38,12 @@ Message::Message(const Shared::Message& source, bool outgoing, const QString& p_ file(0), progress(0), fileComment(new QLabel()), + errorText(""), hasDownloadButton(false), hasProgress(false), hasFile(false), - commentAdded(false) + commentAdded(false), + errorDownloadingFile(false) { body->setBackgroundRole(QPalette::AlternateBase); body->setAutoFillBackground(true); @@ -117,7 +119,12 @@ void Message::addDownloadDialog() } downloadButton = new QPushButton(QIcon::fromTheme("download"), "Download"); downloadButton->setToolTip("" + msg.getOutOfBandUrl() + ""); - fileComment->setText(sender->text() + " is offering you to download a file"); + if (errorDownloadingFile) { + fileComment->setWordWrap(true); + fileComment->setText("Error downloading file: " + errorText + "\nYou can try again"); + } else { + fileComment->setText(sender->text() + " is offering you to download a file"); + } fileComment->show(); connect(downloadButton, SIGNAL(clicked()), this, SLOT(onDownload())); bodyLayout->insertWidget(2, fileComment); @@ -208,6 +215,7 @@ void Message::hideDownload() downloadButton->deleteLater(); downloadButton = 0; hasDownloadButton = false; + errorDownloadingFile = false; } } @@ -228,3 +236,10 @@ void Message::hideProgress() hasProgress = false;; } } + +void Message::showError(const QString& error) +{ + errorDownloadingFile = true; + errorText = error; + addDownloadDialog(); +} diff --git a/ui/widgets/message.h b/ui/widgets/message.h index 362feaf..8a89268 100644 --- a/ui/widgets/message.h +++ b/ui/widgets/message.h @@ -49,6 +49,7 @@ public: void addDownloadDialog(); void showFile(const QString& path); + void showError(const QString& error); void setProgress(qreal value); signals: @@ -66,10 +67,12 @@ private: QLabel* file; QProgressBar* progress; QLabel* fileComment; + QString errorText; bool hasDownloadButton; bool hasProgress; bool hasFile; bool commentAdded; + bool errorDownloadingFile; private slots: void onDownload(); diff --git a/ui/widgets/messageline.cpp b/ui/widgets/messageline.cpp index 7ff8a38..0560344 100644 --- a/ui/widgets/messageline.cpp +++ b/ui/widgets/messageline.cpp @@ -246,3 +246,14 @@ void MessageLine::responseLocalFile(const QString& messageId, const QString& pat } } } + +void MessageLine::downloadError(const QString& messageId, const QString& error) +{ + Index::const_iterator itr = messageIndex.find(messageId); + if (itr == messageIndex.end()) { + + } else { + itr->second->showError(error); + } +} + diff --git a/ui/widgets/messageline.h b/ui/widgets/messageline.h index ce435c7..08cbaa4 100644 --- a/ui/widgets/messageline.h +++ b/ui/widgets/messageline.h @@ -53,6 +53,7 @@ public: void showBusyIndicator(); void hideBusyIndicator(); void responseLocalFile(const QString& messageId, const QString& path); + void downloadError(const QString& messageId, const QString& error); void responseDownloadProgress(const QString& messageId, qreal progress); signals: