summaryrefslogtreecommitdiffstats
path: root/qt-ui
diff options
context:
space:
mode:
authorGravatar Dirk Hohndel <dirk@hohndel.org>2013-12-04 16:21:44 -0800
committerGravatar Dirk Hohndel <dirk@hohndel.org>2013-12-04 16:21:44 -0800
commit421dceba1ed37b5000c4ad5796f4924fb15587b4 (patch)
tree57830cf95bf9ea3af32f2253673c5d145fa76dea /qt-ui
parent28b8d177c3b10d32201615d15c2aba2f969cf3f4 (diff)
parent662e642ac9f650ccafda75d53ff54ff587b3d97c (diff)
downloadsubsurface-421dceba1ed37b5000c4ad5796f4924fb15587b4.tar.gz
Merge branch 'improve-subsurfaceweb' of git://github.com/thiagomacieira/subsurface
Diffstat (limited to 'qt-ui')
-rw-r--r--qt-ui/mainwindow.cpp2
-rw-r--r--qt-ui/subsurfacewebservices.cpp611
-rw-r--r--qt-ui/subsurfacewebservices.h33
-rw-r--r--qt-ui/webservices.ui3
4 files changed, 520 insertions, 129 deletions
diff --git a/qt-ui/mainwindow.cpp b/qt-ui/mainwindow.cpp
index ee77fc72d..e049179e8 100644
--- a/qt-ui/mainwindow.cpp
+++ b/qt-ui/mainwindow.cpp
@@ -269,7 +269,7 @@ void MainWindow::on_actionDownloadWeb_triggered()
void MainWindow::on_actionDivelogs_de_triggered()
{
- DivelogsDeWebServices::instance()->exec();
+ DivelogsDeWebServices::instance()->downloadDives();
}
void MainWindow::on_actionEditDeviceNames_triggered()
diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp
index 1acccda7d..69dd7e1cd 100644
--- a/qt-ui/subsurfacewebservices.cpp
+++ b/qt-ui/subsurfacewebservices.cpp
@@ -3,27 +3,111 @@
#include "mainwindow.h"
#include <libxml/parser.h>
+#include <zip.h>
+#include <QDir>
+#include <QHttpMultiPart>
+#include <QMessageBox>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QDebug>
#include <QSettings>
+#include <QXmlStreamReader>
#include <qdesktopservices.h>
#include "../dive.h"
#include "../divelist.h"
+#ifdef Q_OS_UNIX
+# include <unistd.h> // for dup(2)
+#endif
+
struct dive_table gps_location_table;
static bool merge_locations_into_dives(void);
+static bool is_automatic_fix(struct dive *gpsfix)
+{
+ if (gpsfix && gpsfix->location &&
+ (!strcmp(gpsfix->location, "automatic fix") ||
+ !strcmp(gpsfix->location, "Auto-created dive")))
+ return TRUE;
+ return FALSE;
+}
+
+#define SAME_GROUP 6 * 3600 // six hours
+
+static bool merge_locations_into_dives(void)
+{
+ int i, nr = 0, changed = 0;
+ struct dive *gpsfix, *last_named_fix = NULL, *dive;
+
+ sort_table(&gps_location_table);
+
+ for_each_gps_location(i, gpsfix) {
+ if (is_automatic_fix(gpsfix)) {
+ dive = find_dive_including(gpsfix->when);
+ if (dive && !dive_has_gps_location(dive)) {
+#if DEBUG_WEBSERVICE
+ struct tm tm;
+ utc_mkdate(gpsfix->when, &tm);
+ printf("found dive named %s @ %04d-%02d-%02d %02d:%02d:%02d\n",
+ gpsfix->location,
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+#endif
+ changed++;
+ copy_gps_location(gpsfix, dive);
+ }
+ } else {
+ if (last_named_fix && dive_within_time_range(last_named_fix, gpsfix->when, SAME_GROUP)) {
+ nr++;
+ } else {
+ nr = 1;
+ last_named_fix = gpsfix;
+ }
+ dive = find_dive_n_near(gpsfix->when, nr, SAME_GROUP);
+ if (dive) {
+ if (!dive_has_gps_location(dive)) {
+ copy_gps_location(gpsfix, dive);
+ changed++;
+ }
+ if (!dive->location) {
+ dive->location = strdup(gpsfix->location);
+ changed++;
+ }
+ } else {
+ struct tm tm;
+ utc_mkdate(gpsfix->when, &tm);
+#if DEBUG_WEBSERVICE
+ printf("didn't find dive matching gps fix named %s @ %04d-%02d-%02d %02d:%02d:%02d\n",
+ gpsfix->location,
+ tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+#endif
+ }
+ }
+ }
+ return changed > 0;
+}
+
+static void clear_table(struct dive_table *table)
+{
+ int i;
+ for (i = 0; i < table->nr; i++)
+ free(table->dives[i]);
+ table->nr = 0;
+}
+
WebServices::WebServices(QWidget* parent, Qt::WindowFlags f): QDialog(parent, f)
, reply(0)
{
ui.setupUi(this);
connect(ui.buttonBox, SIGNAL(clicked(QAbstractButton*)), this, SLOT(buttonClicked(QAbstractButton*)));
connect(ui.download, SIGNAL(clicked(bool)), this, SLOT(startDownload()));
+ connect(ui.upload, SIGNAL(clicked(bool)), this, SLOT(startUpload()));
+ connect(&timeout, SIGNAL(timeout()), this, SLOT(downloadTimedOut()));
ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
-
+ timeout.setSingleShot(true);
}
void WebServices::hidePassword()
@@ -35,6 +119,74 @@ void WebServices::hidePassword()
void WebServices::hideUpload()
{
ui.upload->hide();
+ ui.download->show();
+}
+
+void WebServices::hideDownload()
+{
+ ui.download->hide();
+ ui.upload->show();
+}
+
+QNetworkAccessManager *WebServices::manager()
+{
+ static QNetworkAccessManager *manager = new QNetworkAccessManager(qApp);
+ return manager;
+}
+
+void WebServices::downloadTimedOut()
+{
+ if (!reply)
+ return;
+
+ reply->deleteLater();
+ reply = NULL;
+ resetState();
+ ui.status->setText(tr("Operation timed out"));
+}
+
+void WebServices::updateProgress(qint64 current, qint64 total)
+{
+ if (!reply)
+ return;
+
+ if (total >= INT_MAX / 2) {
+ // over a gigabyte!
+ if (total >= Q_INT64_C(1) << 47) {
+ total >>= 16;
+ current >>= 16;
+ }
+ total >>= 16;
+ current >>= 16;
+ }
+ ui.progressBar->setRange(0, total);
+ ui.progressBar->setValue(current);
+ ui.status->setText(tr("Transfering data..."));
+
+ // reset the timer: 30 seconds after we last got any data
+ timeout.start();
+}
+
+void WebServices::connectSignalsForDownload(QNetworkReply *reply)
+{
+ connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
+ this, SLOT(downloadError(QNetworkReply::NetworkError)));
+ connect(reply, SIGNAL(downloadProgress(qint64,qint64)), this,
+ SLOT(updateProgress(qint64,qint64)));
+
+ timeout.start(30000); // 30s
+}
+
+void WebServices::resetState()
+{
+ ui.download->setEnabled(true);
+ ui.upload->setEnabled(true);
+ ui.userID->setEnabled(true);
+ ui.password->setEnabled(true);
+ ui.progressBar->reset();
+ ui.progressBar->setRange(0,1);
+ ui.status->setText(QString());
}
// #
@@ -58,95 +210,85 @@ SubsurfaceWebServices::SubsurfaceWebServices(QWidget* parent, Qt::WindowFlags f)
hideUpload();
}
-static void clear_table(struct dive_table *table)
-{
- int i;
- for (i = 0; i < table->nr; i++)
- free(table->dives[i]);
- table->nr = 0;
-}
-
void SubsurfaceWebServices::buttonClicked(QAbstractButton* button)
{
ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
switch(ui.buttonBox->buttonRole(button)){
- case QDialogButtonBox::ApplyRole:{
- clear_table(&gps_location_table);
- QByteArray url = tr("Webservice").toLocal8Bit();
- parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL, NULL);
-
- /* now merge the data in the gps_location table into the dive_table */
- if (merge_locations_into_dives()) {
- mark_divelist_changed(TRUE);
- }
-
- /* store last entered uid in config */
- QSettings s;
- s.setValue("subsurface_webservice_uid", ui.userID->text().toUpper());
- s.sync();
- hide();
- close();
+ case QDialogButtonBox::ApplyRole:{
+ clear_table(&gps_location_table);
+ QByteArray url = tr("Webservice").toLocal8Bit();
+ parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL, NULL);
+
+ /* now merge the data in the gps_location table into the dive_table */
+ if (merge_locations_into_dives()) {
+ mark_divelist_changed(TRUE);
}
+
+ /* store last entered uid in config */
+ QSettings s;
+ s.setValue("subsurface_webservice_uid", ui.userID->text().toUpper());
+ s.sync();
+ hide();
+ close();
+ resetState();
+ }
+ break;
+ case QDialogButtonBox::RejectRole:
+ // we may want to clean up after ourselves
+ // reply->deleteLater();
+ reply = NULL;
+ resetState();
+ break;
+ case QDialogButtonBox::HelpRole:
+ QDesktopServices::openUrl(QUrl("http://api.hohndel.org"));
+ break;
+ default:
break;
- case QDialogButtonBox::RejectRole:
- // we may want to clean up after ourselves, but this
- // makes Subsurface throw a SIGSEGV...
- // manager->deleteLater();
- // reply->deleteLater();
- ui.progressBar->setMaximum(1);
- break;
- case QDialogButtonBox::HelpRole:
- QDesktopServices::openUrl(QUrl("http://api.hohndel.org"));
- break;
- default:
- break;
}
}
void SubsurfaceWebServices::startDownload()
{
QUrl url("http://api.hohndel.org/api/dive/get/");
- url.setQueryItems( QList<QPair<QString,QString> >() << qMakePair(QString("login"), ui.userID->text().toUpper()));
+ url.addQueryItem("login", ui.userID->text().toUpper());
- manager = new QNetworkAccessManager(this);
QNetworkRequest request;
request.setUrl(url);
request.setRawHeader("Accept", "text/xml");
- reply = manager->get(request);
- ui.status->setText(tr("Wait a bit until we have something..."));
+ reply = manager()->get(request);
+ ui.status->setText(tr("Connecting..."));
ui.progressBar->setRange(0,0); // this makes the progressbar do an 'infinite spin'
ui.download->setEnabled(false);
ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
-
- connect(reply, SIGNAL(finished()), this, SLOT(downloadFinished()));
- connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
- this, SLOT(downloadError(QNetworkReply::NetworkError)));
+ connectSignalsForDownload(reply);
}
void SubsurfaceWebServices::downloadFinished()
{
+ if (!reply)
+ return;
+
ui.progressBar->setRange(0,1);
downloadedData = reply->readAll();
ui.download->setEnabled(true);
- ui.status->setText(tr("Download Finished"));
+ ui.status->setText(tr("Download finished"));
uint resultCode = download_dialog_parse_response(downloadedData);
setStatusText(resultCode);
if (resultCode == DD_STATUS_OK){
ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
}
- manager->deleteLater();
reply->deleteLater();
+ reply = NULL;
}
-void SubsurfaceWebServices::downloadError(QNetworkReply::NetworkError error)
+void SubsurfaceWebServices::downloadError(QNetworkReply::NetworkError)
{
- ui.download->setEnabled(true);
- ui.progressBar->setRange(0,1);
- ui.status->setText(QString::number((int)QNetworkRequest::HttpStatusCodeAttribute));
- manager->deleteLater();
+ resetState();
+ ui.status->setText(tr("Download error: %1").arg(reply->errorString()));
reply->deleteLater();
+ reply = NULL;
}
void SubsurfaceWebServices::setStatusText(int status)
@@ -167,7 +309,7 @@ void SubsurfaceWebServices::download_dialog_traverse_xml(xmlNodePtr node, unsign
xmlNodePtr cur_node;
for (cur_node = node; cur_node; cur_node = cur_node->next) {
if ((!strcmp((const char *)cur_node->name, (const char *)"download")) &&
- (!strcmp((const char *)xmlNodeGetContent(cur_node), (const char *)"ok"))) {
+ (!strcmp((const char *)xmlNodeGetContent(cur_node), (const char *)"ok"))) {
*download_status = DD_STATUS_OK;
return;
} else if (!strcmp((const char *)cur_node->name, (const char *)"error")) {
@@ -192,74 +334,9 @@ unsigned int SubsurfaceWebServices::download_dialog_parse_response(const QByteAr
}
if (root->children)
download_dialog_traverse_xml(root->children, &status);
- end:
- xmlFreeDoc(doc);
- return status;
-}
-
-static bool is_automatic_fix(struct dive *gpsfix)
-{
- if (gpsfix && gpsfix->location &&
- (!strcmp(gpsfix->location, "automatic fix") ||
- !strcmp(gpsfix->location, "Auto-created dive")))
- return TRUE;
- return FALSE;
-}
-
-#define SAME_GROUP 6 * 3600 // six hours
-
-static bool merge_locations_into_dives(void)
-{
- int i, nr = 0, changed = 0;
- struct dive *gpsfix, *last_named_fix = NULL, *dive;
-
- sort_table(&gps_location_table);
-
- for_each_gps_location(i, gpsfix) {
- if (is_automatic_fix(gpsfix)) {
- dive = find_dive_including(gpsfix->when);
- if (dive && !dive_has_gps_location(dive)) {
-#if DEBUG_WEBSERVICE
- struct tm tm;
- utc_mkdate(gpsfix->when, &tm);
- printf("found dive named %s @ %04d-%02d-%02d %02d:%02d:%02d\n",
- gpsfix->location,
- tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
-#endif
- changed++;
- copy_gps_location(gpsfix, dive);
- }
- } else {
- if (last_named_fix && dive_within_time_range(last_named_fix, gpsfix->when, SAME_GROUP)) {
- nr++;
- } else {
- nr = 1;
- last_named_fix = gpsfix;
- }
- dive = find_dive_n_near(gpsfix->when, nr, SAME_GROUP);
- if (dive) {
- if (!dive_has_gps_location(dive)) {
- copy_gps_location(gpsfix, dive);
- changed++;
- }
- if (!dive->location) {
- dive->location = strdup(gpsfix->location);
- changed++;
- }
- } else {
- struct tm tm;
- utc_mkdate(gpsfix->when, &tm);
-#if DEBUG_WEBSERVICE
- printf("didn't find dive matching gps fix named %s @ %04d-%02d-%02d %02d:%02d:%02d\n",
- gpsfix->location,
- tm.tm_year+1900, tm.tm_mon+1, tm.tm_mday,
- tm.tm_hour, tm.tm_min, tm.tm_sec);
-#endif
- }
- }
- }
- return changed > 0;
+end:
+ xmlFreeDoc(doc);
+ return status;
}
// #
@@ -268,6 +345,92 @@ static bool merge_locations_into_dives(void)
// #
// #
+struct DiveListResult
+{
+ QString errorCondition;
+ QString errorDetails;
+ QByteArray idList; // comma-separated, suitable to be sent in the fetch request
+ int idCount;
+};
+
+static DiveListResult parseDiveLogsDeDiveList(const QByteArray &xmlData)
+{
+ /* XML format seems to be:
+ * <DiveDateReader version="1.0">
+ * <DiveDates>
+ * <date diveLogsId="nnn" lastModified="YYYY-MM-DD hh:mm:ss">DD.MM.YYYY hh:mm</date>
+ * [repeat <date></date>]
+ * </DiveDates>
+ * </DiveDateReader>
+ */
+ QXmlStreamReader reader(xmlData);
+ const QString invalidXmlError = DivelogsDeWebServices::tr("Invalid response from server");
+ bool seenDiveDates = false;
+ DiveListResult result;
+ result.idCount = 0;
+
+ if (reader.readNextStartElement() && reader.name() != "DiveDateReader") {
+ result.errorCondition = invalidXmlError;
+ result.errorDetails =
+ DivelogsDeWebServices::tr("Expected XML tag 'DiveDateReader', got instead '%1")
+ .arg(reader.name().toString());
+ goto out;
+ }
+
+ while (reader.readNextStartElement()) {
+ if (reader.name() != "DiveDates") {
+ if (reader.name() == "Login") {
+ QString status = reader.readElementText();
+ // qDebug() << "Login status:" << status;
+
+ // Note: there has to be a better way to determine a successful login...
+ if (status == "failed") {
+ result.errorCondition = "Login failed";
+ goto out;
+ }
+ } else {
+ // qDebug() << "Skipping" << reader.name();
+ }
+ continue;
+ }
+
+ // process <DiveDates>
+ seenDiveDates = true;
+ while (reader.readNextStartElement()) {
+ if (reader.name() != "date") {
+ // qDebug() << "Skipping" << reader.name();
+ continue;
+ }
+ QStringRef id = reader.attributes().value("divelogsId");
+ // qDebug() << "Found" << reader.name() << "with id =" << id;
+ if (!id.isEmpty()) {
+ result.idList += id.toLatin1();
+ result.idList += ',';
+ ++result.idCount;
+ }
+
+ reader.skipCurrentElement();
+ }
+ }
+
+ // chop the ending comma, if any
+ result.idList.chop(1);
+
+ if (!seenDiveDates) {
+ result.errorCondition = invalidXmlError;
+ result.errorDetails = DivelogsDeWebServices::tr("Expected XML tag 'DiveDates' not found");
+ }
+
+out:
+ if (reader.hasError()) {
+ // if there was an XML error, overwrite the result or other error conditions
+ result.errorCondition = invalidXmlError;
+ result.errorDetails = DivelogsDeWebServices::tr("Malformed XML response. Line %1: %2")
+ .arg(reader.lineNumber()).arg(reader.errorString());
+ }
+ return result;
+}
+
DivelogsDeWebServices* DivelogsDeWebServices::instance()
{
static DivelogsDeWebServices *self = new DivelogsDeWebServices(mainWindow());
@@ -275,24 +438,217 @@ DivelogsDeWebServices* DivelogsDeWebServices::instance()
return self;
}
-DivelogsDeWebServices::DivelogsDeWebServices(QWidget* parent, Qt::WindowFlags f): WebServices(parent, f)
+void DivelogsDeWebServices::downloadDives()
{
+ hideUpload();
+ exec();
+}
+void DivelogsDeWebServices::uploadDives(QIODevice *dldContent)
+{
+ QHttpMultiPart mp(QHttpMultiPart::FormDataType);
+ QHttpPart part;
+ part.setRawHeader("Content-Disposition", "form-data; name=\"userfile\"");
+ part.setBodyDevice(dldContent);
+ mp.append(part);
+
+ multipart = &mp;
+ hideDownload();
+ exec();
+ multipart = NULL;
+
+ delete reply; // we need to ensure it has stopped using our QHttpMultiPart
+}
+
+DivelogsDeWebServices::DivelogsDeWebServices(QWidget* parent, Qt::WindowFlags f): WebServices(parent, f)
+{
+ QSettings s;
+ ui.userID->setText(s.value("divelogde_user").toString());
+ ui.password->setText(s.value("divelogde_pass").toString());
+ hideUpload();
}
void DivelogsDeWebServices::startUpload()
{
+ ui.status->setText(tr("Uploading dive list..."));
+ ui.progressBar->setRange(0,0); // this makes the progressbar do an 'infinite spin'
+ ui.upload->setEnabled(false);
+ ui.userID->setEnabled(false);
+ ui.password->setEnabled(false);
+ QNetworkRequest request;
+ request.setUrl(QUrl("https://divelogs.de/DivelogsDirectImport.php"));
+ request.setRawHeader("Accept", "text/xml, application/xml");
+
+ QHttpPart part;
+ part.setRawHeader("Content-Disposition", "form-data; name=\"user\"");
+ part.setBody(ui.userID->text().toUtf8());
+ multipart->append(part);
+
+ part.setRawHeader("Content-Disposition", "form-data; name=\"pass\"");
+ part.setBody(ui.password->text().toUtf8());
+ multipart->append(part);
+
+ reply = manager()->post(request, multipart);
+ connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
+ SLOT(uploadError(QNetworkReply::NetworkError)));
+ connect(reply, SIGNAL(uploadProgress(qint64,qint64)), this,
+ SLOT(updateProgress(qint64,qint64)));
+
+ timeout.start(30000); // 30s
}
void DivelogsDeWebServices::startDownload()
{
+ ui.status->setText(tr("Downloading dive list..."));
+ ui.progressBar->setRange(0,0); // this makes the progressbar do an 'infinite spin'
+ ui.download->setEnabled(false);
+ ui.userID->setEnabled(false);
+ ui.password->setEnabled(false);
+
+ QNetworkRequest request;
+ request.setUrl(QUrl("https://divelogs.de/xml_available_dives.php"));
+ request.setRawHeader("Accept", "text/xml, application/xml");
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ QUrl body;
+ body.addQueryItem("user", ui.userID->text());
+ body.addQueryItem("pass", ui.password->text());
+
+ reply = manager()->post(request, body.encodedQuery());
+#else
+ QUrlQuery body;
+ body.addQueryItem("user", ui.userID->text());
+ body.addQueryItem("pass", ui.password->text());
+
+ reply = manager()->post(request, body.query(QUrl::FullyEncoded).toLatin1())
+#endif
+ connect(reply, SIGNAL(finished()), this, SLOT(listDownloadFinished()));
+ connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
+ this, SLOT(downloadError(QNetworkReply::NetworkError)));
+
+ timeout.start(30000); // 30s
+}
+
+void DivelogsDeWebServices::listDownloadFinished()
+{
+ if (!reply)
+ return;
+ QByteArray xmlData = reply->readAll();
+ reply->deleteLater();
+ reply = NULL;
+
+ // parse the XML data we downloaded
+ DiveListResult diveList = parseDiveLogsDeDiveList(xmlData);
+ if (!diveList.errorCondition.isEmpty()) {
+ // error condition
+ resetState();
+ ui.status->setText(diveList.errorCondition);
+ return;
+ }
+
+ ui.status->setText(tr("Downloading %1 dives...").arg(diveList.idCount));
+
+ QNetworkRequest request;
+// request.setUrl(QUrl("https://divelogs.de/DivelogsDirectExport.php"));
+ request.setUrl(QUrl("http://divelogs.de/DivelogsDirectExport.php"));
+ request.setRawHeader("Accept", "application/zip, */*");
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ QUrl body;
+ body.addQueryItem("user", ui.userID->text());
+ body.addQueryItem("pass", ui.password->text());
+ body.addQueryItem("ids", diveList.idList);
+
+ reply = manager()->post(request, body.encodedQuery());
+#else
+ QUrlQuery body;
+ body.addQueryItem("user", ui.userID->text());
+ body.addQueryItem("pass", ui.password->text());
+ body.addQueryItem("ids", diveList.idList);
+
+ reply = manager()->post(request, body.query(QUrl::FullyEncoded).toLatin1())
+#endif
+
+ connect(reply, SIGNAL(readyRead()), this, SLOT(saveToZipFile()));
+ connectSignalsForDownload(reply);
+}
+
+void DivelogsDeWebServices::saveToZipFile()
+{
+ if (!zipFile.isOpen()) {
+ zipFile.setFileTemplate(QDir::tempPath() + "/import-XXXXXX.dld");
+ zipFile.open();
+ }
+ zipFile.write(reply->readAll());
}
void DivelogsDeWebServices::downloadFinished()
{
+ if (!reply)
+ return;
+
+ ui.download->setEnabled(true);
+ ui.status->setText(tr("Download finished - %1").arg(reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString()));
+ reply->deleteLater();
+ reply = NULL;
+
+ int errorcode;
+ zipFile.seek(0);
+#ifdef Q_OS_UNIX
+ int duppedfd = dup(zipFile.handle());
+ struct zip *zip = zip_fdopen(duppedfd, 0, &errorcode);
+ if (!zip)
+ ::close(duppedfd);
+#else
+ struct zip *zip = zip_open(zipFile.fileName().toLocal8Bit().data(), 0, &errorcode);
+#endif
+ if (!zip) {
+ char buf[512];
+ zip_error_to_str(buf, sizeof(buf), errorcode, errno);
+ QMessageBox::critical(this, tr("Corrupted download"),
+ tr("The archive could not be opened:\n%1").arg(QString::fromLocal8Bit(buf)));
+ zipFile.close();
+ return;
+ }
+
+ quint64 entries = zip_get_num_entries(zip, 0);
+ for (quint64 i = 0; i < entries; ++i) {
+ struct zip_file *zip_file = zip_fopen_index(zip, i, 0);
+ if (!zip_file) {
+ QMessageBox::critical(this, tr("Corrupted download"),
+ tr("The archive contains corrupt data:\n%1").arg(QString::fromLocal8Bit(zip_strerror(zip))));
+ goto close_zip;
+ }
+ // ### FIXME: What do I do with this?
+
+ zip_fclose(zip_file);
+ }
+
+close_zip:
+ zip_close(zip);
+ zipFile.close();
+}
+
+void DivelogsDeWebServices::uploadFinished()
+{
+ if (!reply)
+ return;
+
+ ui.progressBar->setRange(0,1);
+ ui.upload->setEnabled(true);
+ ui.status->setText(tr("Upload finished"));
+
+ // check what the server sent us: it might contain
+ // an error condition, such as a failed login
+ QByteArray xmlData = reply->readAll();
+
+ // ### FIXME: what's the format?
}
void DivelogsDeWebServices::setStatusText(int status)
@@ -300,12 +656,21 @@ void DivelogsDeWebServices::setStatusText(int status)
}
-void DivelogsDeWebServices::downloadError(QNetworkReply::NetworkError error)
+void DivelogsDeWebServices::downloadError(QNetworkReply::NetworkError)
{
+ resetState();
+ ui.status->setText(tr("Download error: %1").arg(reply->errorString()));
+ reply->deleteLater();
+ reply = NULL;
+}
+void DivelogsDeWebServices::uploadError(QNetworkReply::NetworkError error)
+{
+ downloadError(error);
}
void DivelogsDeWebServices::buttonClicked(QAbstractButton* button)
{
}
+
diff --git a/qt-ui/subsurfacewebservices.h b/qt-ui/subsurfacewebservices.h
index 515e3fe9c..3c41b08d1 100644
--- a/qt-ui/subsurfacewebservices.h
+++ b/qt-ui/subsurfacewebservices.h
@@ -3,29 +3,43 @@
#include <QDialog>
#include <QNetworkReply>
+#include <QTemporaryFile>
+#include <QTimer>
#include <libxml/tree.h>
#include "ui_webservices.h"
class QAbstractButton;
class QNetworkReply;
+class QHttpMultiPart;
class WebServices : public QDialog{
Q_OBJECT
public:
- explicit WebServices(QWidget* parent = 0, Qt::WindowFlags f = 0);
+ explicit WebServices(QWidget* parent = 0, Qt::WindowFlags f = 0);
void hidePassword();
void hideUpload();
+ void hideDownload();
+
+ static QNetworkAccessManager *manager();
private slots:
virtual void startDownload() = 0;
virtual void startUpload() = 0;
virtual void buttonClicked(QAbstractButton* button) = 0;
+ virtual void downloadTimedOut();
+
+protected slots:
+ void updateProgress(qint64 current, qint64 total);
protected:
+ void resetState();
+ void connectSignalsForDownload(QNetworkReply *reply);
+ void connectSignalsForUpload();
+
Ui::WebServices ui;
QNetworkReply *reply;
- QNetworkAccessManager *manager;
+ QTimer timeout;
QByteArray downloadedData;
};
@@ -41,7 +55,7 @@ private slots:
void downloadError(QNetworkReply::NetworkError error);
void startUpload(){} /*no op*/
private:
- explicit SubsurfaceWebServices(QWidget* parent = 0, Qt::WindowFlags f = 0);
+ explicit SubsurfaceWebServices(QWidget* parent = 0, Qt::WindowFlags f = 0);
void setStatusText(int status);
void download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status);
unsigned int download_dialog_parse_response(const QByteArray& length);
@@ -51,18 +65,27 @@ class DivelogsDeWebServices : public WebServices {
Q_OBJECT
public:
static DivelogsDeWebServices * instance();
+ void downloadDives();
+ void uploadDives(QIODevice *dldContent);
private slots:
void startDownload();
void buttonClicked(QAbstractButton* button);
+ void saveToZipFile();
+ void listDownloadFinished();
void downloadFinished();
+ void uploadFinished();
void downloadError(QNetworkReply::NetworkError error);
- void startUpload();
+ void uploadError(QNetworkReply::NetworkError error);
+ void startUpload();
private:
- explicit DivelogsDeWebServices (QWidget* parent = 0, Qt::WindowFlags f = 0);
+ explicit DivelogsDeWebServices (QWidget* parent = 0, Qt::WindowFlags f = 0);
void setStatusText(int status);
void download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status);
unsigned int download_dialog_parse_response(const QByteArray& length);
+
+ QHttpMultiPart *multipart;
+ QTemporaryFile zipFile;
};
#endif
diff --git a/qt-ui/webservices.ui b/qt-ui/webservices.ui
index 123a95eae..942ff1fa3 100644
--- a/qt-ui/webservices.ui
+++ b/qt-ui/webservices.ui
@@ -69,6 +69,9 @@
<property name="text">
<string/>
</property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
+ </property>
</widget>
</item>
<item row="1" column="0">