From be16ade0383813949e82491de5a26f42752c3e0f Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Sat, 7 Dec 2013 14:33:43 +0200 Subject: Divelogs.de: allow import of downloaded dives This patch adds commands to the dialog buttons, to apply (and possibly show an error if 'parse_file' fails', reject, or show help - which is a link to the 'divelogs.de' website. Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 193f7c41b..08976e52c 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -1,7 +1,6 @@ #include "subsurfacewebservices.h" #include "../webservice.h" #include "mainwindow.h" - #include #include #include @@ -663,22 +662,39 @@ void DivelogsDeWebServices::uploadError(QNetworkReply::NetworkError error) void DivelogsDeWebServices::buttonClicked(QAbstractButton* button) { ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); - switch(ui.buttonBox->buttonRole(button)){ case QDialogButtonBox::ApplyRole:{ - char *errorptr = NULL; - parse_file(zipFile.fileName().toUtf8().constData(), &errorptr); + /* parse file and import dives */ + char *error = NULL; + parse_file(zipFile.fileName().toLocal8Bit().data(), &error); + if (error != NULL) { + mainWindow()->showError(error); + free(error); + } process_dives(TRUE, FALSE); - // ### FIXME: do something useful with the error - but there shouldn't be one, right? - if (errorptr) - qDebug() << errorptr; + mainWindow()->refreshDisplay(); + /* store last entered user/pass in config */ + QSettings s; + s.setValue("divelogde_user", ui.userID->text()); + s.setValue("divelogde_pass", ui.password->text()); + s.sync(); hide(); close(); resetState(); - mark_divelist_changed(TRUE); - mainWindow()->refreshDisplay(); } + break; + case QDialogButtonBox::RejectRole: + // these two seem to be causing a crash: + // reply->deleteLater(); + // reply = NULL; + resetState(); + break; + case QDialogButtonBox::HelpRole: + QDesktopServices::openUrl(QUrl("http://divelogs.de")); + break; + default: + break; } } -- cgit v1.2.3-70-g09d2 From 0905e4f0ab5eb9452af84d57e6bdf349fbf879ef Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Thu, 5 Dec 2013 18:31:40 +0200 Subject: Divelogs.de: add code for zipping up all dives prepare_dives_for_divelogs() comes from the GTK version, originally. The upload seems to fail at this point with an error. Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 106 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 08976e52c..5c2fa6d12 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -98,6 +98,81 @@ static void clear_table(struct dive_table *table) table->nr = 0; } +static char *prepare_dives_for_divelogs(const bool selected) +{ + int i; + struct dive *dive; + FILE *f; + char filename[PATH_MAX], *tempfile; + size_t streamsize; + char *membuf; + xmlDoc *doc; + xsltStylesheetPtr xslt = NULL; + xmlDoc *transformed; + struct zip_source *s[dive_table.nr]; + struct zip *zip; + char *error = NULL; + char *parsed = NULL, *endat = NULL; + + QTemporaryFile zipFile; + zipFile.setFileTemplate(QDir::tempPath() + "/import-XXXXXX.dld"); + tempfile = zipFile.fileName().toLocal8Bit().data(); + zip = zip_open(tempfile, ZIP_CREATE, NULL); + + if (!zip || !amount_selected) + return NULL; + + /* walk the dive list in chronological order */ + for (i = 0; i < dive_table.nr; i++) { + dive = get_dive(i); + if (!dive) + continue; + if (selected && !dive->selected) + continue; + f = tmpfile(); + if (!f) + return NULL; + save_dive(f, dive); + fseek(f, 0, SEEK_END); + streamsize = ftell(f); + rewind(f); + membuf = (char *)malloc(streamsize + 1); + if (!membuf || !fread(membuf, streamsize, 1, f)) + return NULL; + membuf[streamsize] = 0; + fclose(f); + /* + * Parse the memory buffer into XML document and + * transform it to divelogs.de format, finally dumping + * the XML into a character buffer. + */ + doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0); + if (!doc) + continue; + free((void *)membuf); + xslt = get_stylesheet("divelogs-export.xslt"); + if (!xslt) + return NULL; + transformed = xsltApplyStylesheet(xslt, doc, NULL); + xsltFreeStylesheet(xslt); + xmlDocDumpMemory(transformed, (xmlChar **) &membuf, (int *)&streamsize); + xmlFreeDoc(doc); + xmlFreeDoc(transformed); + /* + * Save the XML document into a zip file. + */ + snprintf(filename, PATH_MAX, "%d.xml", i + 1); + s[i] = zip_source_buffer(zip, membuf, streamsize, 1); + if (s[i]) { + int64_t ret = zip_add(zip, filename, s[i]); + if (ret == -1) + fprintf(stderr, "failed to include dive %d\n", i); + } + } + zip_close(zip); + return tempfile; +} + WebServices::WebServices(QWidget* parent, Qt::WindowFlags f): QDialog(parent, f) , reply(0) { @@ -446,6 +521,18 @@ void DivelogsDeWebServices::downloadDives() exec(); } +void DivelogsDeWebServices::prepareDivesForUpload() +{ + char *file = prepare_dives_for_divelogs(true); + if (file) { + QFile f(file); + free(file); + uploadDives((QIODevice *)&f); + return; + } + // FIXME: show error +} + void DivelogsDeWebServices::uploadDives(QIODevice *dldContent) { QHttpMultiPart mp(QHttpMultiPart::FormDataType); @@ -639,6 +726,25 @@ void DivelogsDeWebServices::uploadFinished() QByteArray xmlData = reply->readAll(); // ### FIXME: what's the format? + /* + // char *error; + if (error) { + parsed = strstr(error, ""); + endat = strstr(error, ""); + if (parsed && endat) + *endat = '\0'; + } + if (error && strstr(error, "failed")) + type = GTK_MESSAGE_ERROR; + else + type = GTK_MESSAGE_INFO; + } + if (parsed) + divelogs_status_dialog(parsed, type); + else if (error) + divelogs_status_dialog(error, type); + free(error); + */ } void DivelogsDeWebServices::setStatusText(int status) -- cgit v1.2.3-70-g09d2 From 231e02f97ddcf355855190ed61c71e54c1ab5157 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Sat, 7 Dec 2013 14:56:03 +0200 Subject: Divelogs.de: parse response once the upload is finished This is based on the GTK version. Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 5c2fa6d12..e92dd431c 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -724,27 +724,19 @@ void DivelogsDeWebServices::uploadFinished() // 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? - /* - // char *error; - if (error) { - parsed = strstr(error, ""); - endat = strstr(error, ""); - if (parsed && endat) - *endat = '\0'; + char *resp = xmlData.data(); + if (resp) { + char *parsed = strstr(resp, ""); + // char *endat = strstr(resp, ""); + if (parsed) { + if (strstr(resp, "failed")) + ui.status->setText(tr("Login failed")); + else + ui.status->setText(tr("Upload successful")); + } else { + ui.status->setText(tr("Cannot parse response")); } - if (error && strstr(error, "failed")) - type = GTK_MESSAGE_ERROR; - else - type = GTK_MESSAGE_INFO; } - if (parsed) - divelogs_status_dialog(parsed, type); - else if (error) - divelogs_status_dialog(error, type); - free(error); - */ } void DivelogsDeWebServices::setStatusText(int status) -- cgit v1.2.3-70-g09d2 From 551449986701ccfc23cf32388648ec43b4ae1338 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Sat, 7 Dec 2013 16:43:28 +0200 Subject: Divelogs.de: improve prepare_dives_for_divelogs() prepare_dives_for_divelogs() is based on GTK / C code where we used GLib methods to generate a temp zip file. Qt has QTemporaryFile, but it seems there is some sort of a problem when using with with zip_open (ZIP_CREATE considered) or at least in this particular case. To workaround that, we generate a random name ourself with qrand() and simply pass it to zip_open (with ZIP_CREATE) and then return the filename. Also (!), there is memory corruption when trying to return 'tempfile'. This wasn't the case with the C compiler, to my knowledge. Regardless of this fact the generated zip does not look corrupt. Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 55 +++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 18 deletions(-) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index e92dd431c..1a1798e16 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -112,15 +112,20 @@ static char *prepare_dives_for_divelogs(const bool selected) struct zip_source *s[dive_table.nr]; struct zip *zip; char *error = NULL; - char *parsed = NULL, *endat = NULL; - QTemporaryFile zipFile; - zipFile.setFileTemplate(QDir::tempPath() + "/import-XXXXXX.dld"); - tempfile = zipFile.fileName().toLocal8Bit().data(); + /* generate a random filename and create/open that file with zip_open */ + QString tempfileQ = QDir::tempPath() + "/import-" + QString::number(qrand() % 99999999) + ".dld"; + tempfile = tempfileQ.toLocal8Bit().data(); zip = zip_open(tempfile, ZIP_CREATE, NULL); - if (!zip || !amount_selected) + if (!zip) { + fprintf(stderr, "divelog.de-upload: cannot open file as zip\n"); + return NULL; + } + if (!amount_selected) { + fprintf(stderr, "divelog.de-upload: no dives selected\n"); return NULL; + } /* walk the dive list in chronological order */ for (i = 0; i < dive_table.nr; i++) { @@ -130,15 +135,19 @@ static char *prepare_dives_for_divelogs(const bool selected) if (selected && !dive->selected) continue; f = tmpfile(); - if (!f) + if (!f) { + fprintf(stderr, "divelog.de-upload: cannot create temp file\n"); return NULL; + } save_dive(f, dive); fseek(f, 0, SEEK_END); streamsize = ftell(f); rewind(f); membuf = (char *)malloc(streamsize + 1); - if (!membuf || !fread(membuf, streamsize, 1, f)) + if (!membuf || !fread(membuf, streamsize, 1, f)) { + fprintf(stderr, "divelog.de-upload: memory error\n"); return NULL; + } membuf[streamsize] = 0; fclose(f); /* @@ -147,12 +156,17 @@ static char *prepare_dives_for_divelogs(const bool selected) * the XML into a character buffer. */ doc = xmlReadMemory(membuf, strlen(membuf), "divelog", NULL, 0); - if (!doc) + if (!doc) { + fprintf(stderr, "divelog.de-upload: xml error\n"); continue; + } free((void *)membuf); + // this call is overriding our local variable tempfile! not a good sign! xslt = get_stylesheet("divelogs-export.xslt"); - if (!xslt) + if (!xslt) { + fprintf(stderr, "divelog.de-upload: missing stylesheet\n"); return NULL; + } transformed = xsltApplyStylesheet(xslt, doc, NULL); xsltFreeStylesheet(xslt); xmlDocDumpMemory(transformed, (xmlChar **) &membuf, (int *)&streamsize); @@ -166,10 +180,12 @@ static char *prepare_dives_for_divelogs(const bool selected) if (s[i]) { int64_t ret = zip_add(zip, filename, s[i]); if (ret == -1) - fprintf(stderr, "failed to include dive %d\n", i); + fprintf(stderr, "divelog.de-upload: failed to include dive %d\n", i); } } zip_close(zip); + /* let's call this again */ + tempfile = tempfileQ.toLocal8Bit().data(); return tempfile; } @@ -523,14 +539,17 @@ void DivelogsDeWebServices::downloadDives() void DivelogsDeWebServices::prepareDivesForUpload() { - char *file = prepare_dives_for_divelogs(true); - if (file) { - QFile f(file); - free(file); - uploadDives((QIODevice *)&f); - return; + char *filename = prepare_dives_for_divelogs(true); + if (filename) { + QFile f(filename); + if (f.exists()) { + uploadDives((QIODevice *)&f); + f.close(); + f.remove(); + return; + } + mainWindow()->showError(QString("Cannot create file: ").append(filename)); } - // FIXME: show error } void DivelogsDeWebServices::uploadDives(QIODevice *dldContent) @@ -725,6 +744,7 @@ void DivelogsDeWebServices::uploadFinished() // an error condition, such as a failed login QByteArray xmlData = reply->readAll(); char *resp = xmlData.data(); + // qDebug() << resp; if (resp) { char *parsed = strstr(resp, ""); // char *endat = strstr(resp, ""); @@ -795,4 +815,3 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton* button) break; } } - -- cgit v1.2.3-70-g09d2 From 70fce6ce6b37ea0372ea8072fcea3672344d43a4 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Sat, 7 Dec 2013 17:10:58 +0200 Subject: Divelogs.de: further improve reading the server reponse We now check for the following entries: succeeded failed Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 1a1798e16..3b1b25443 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -543,6 +543,7 @@ void DivelogsDeWebServices::prepareDivesForUpload() if (filename) { QFile f(filename); if (f.exists()) { + f.open(QIODevice::ReadOnly); uploadDives((QIODevice *)&f); f.close(); f.remove(); @@ -744,18 +745,21 @@ void DivelogsDeWebServices::uploadFinished() // an error condition, such as a failed login QByteArray xmlData = reply->readAll(); char *resp = xmlData.data(); - // qDebug() << resp; if (resp) { char *parsed = strstr(resp, ""); - // char *endat = strstr(resp, ""); if (parsed) { - if (strstr(resp, "failed")) - ui.status->setText(tr("Login failed")); - else + if (strstr(resp, "succeeded")) { + if (strstr(resp, "failed")) { + ui.status->setText(tr("Upload failed")); + return; + } ui.status->setText(tr("Upload successful")); - } else { - ui.status->setText(tr("Cannot parse response")); + return; + } + ui.status->setText(tr("Login failed")); + return; } + ui.status->setText(tr("Cannot parse response")); } } -- cgit v1.2.3-70-g09d2 From ca731cef893d4bdee8eab60c4e279ec3b83aba0d Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 9 Dec 2013 15:29:57 +0200 Subject: Divelogs.de: Add a 'filename' field when uploading The 'Content-Disposition' header, requires that we pass 'name' but also a 'filename' field. Suggested-by: Sergey Starosek Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 3b1b25443..952927e8c 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -557,7 +557,10 @@ void DivelogsDeWebServices::uploadDives(QIODevice *dldContent) { QHttpMultiPart mp(QHttpMultiPart::FormDataType); QHttpPart part; - part.setRawHeader("Content-Disposition", "form-data; name=\"userfile\""); + QFile *f = (QFile *)dldContent; + QFileInfo fi(*f); + QString args("form-data; name=\"userfile\"; filename=\"" + fi.absoluteFilePath() + "\""); + part.setRawHeader("Content-Disposition", args.toLatin1()); part.setBodyDevice(dldContent); mp.append(part); -- cgit v1.2.3-70-g09d2 From de8d532adba6597b5c60b7935896b9238f5600d6 Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 9 Dec 2013 16:07:00 +0200 Subject: Divelogs.de: Improve the error handling post DLD creation Only show a filename in the error report if such was previsly set. We also add a string for translation, that is shown in the main window. Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 952927e8c..c114c80eb 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -539,6 +539,7 @@ void DivelogsDeWebServices::downloadDives() void DivelogsDeWebServices::prepareDivesForUpload() { + QString errorText(tr("Cannot create DLD file")); char *filename = prepare_dives_for_divelogs(true); if (filename) { QFile f(filename); @@ -549,8 +550,10 @@ void DivelogsDeWebServices::prepareDivesForUpload() f.remove(); return; } - mainWindow()->showError(QString("Cannot create file: ").append(filename)); + mainWindow()->showError(errorText.append(": ").append(filename)); + return; } + mainWindow()->showError(errorText.append("!")); } void DivelogsDeWebServices::uploadDives(QIODevice *dldContent) -- cgit v1.2.3-70-g09d2 From e0acad42aa7dcee4fdea940d85c9bfeca20b0c4d Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 9 Dec 2013 16:45:54 +0200 Subject: Divelogs.de: store the user/pass on upload We store the user/pass for 'Apply' when downloading, but we also want to store these values for 'Upload'. Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index c114c80eb..13a5a8a87 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -585,6 +585,11 @@ DivelogsDeWebServices::DivelogsDeWebServices(QWidget* parent, Qt::WindowFlags f) void DivelogsDeWebServices::startUpload() { + QSettings s; + s.setValue("divelogde_user", ui.userID->text()); + s.setValue("divelogde_pass", ui.password->text()); + s.sync(); + ui.status->setText(tr("Uploading dive list...")); ui.progressBar->setRange(0,0); // this makes the progressbar do an 'infinite spin' ui.upload->setEnabled(false); -- cgit v1.2.3-70-g09d2 From 04b62a46e9f28f0e3b4a4f2e3c3aa9c18445df2f Mon Sep 17 00:00:00 2001 From: "Lubomir I. Ivanov" Date: Mon, 9 Dec 2013 17:42:06 +0200 Subject: Divelogs.de: attempt to impletent 'Cancel' properly The correct way to stop the upload/download is to use reply->abort(). If the dialog closes, post exec() we check if the reply 'isOpen' and abort and delete it. Without this modification the program seems to crash as the connection is still in action and it attempts to read an already deleted file. Signed-off-by: Lubomir I. Ivanov --- qt-ui/subsurfacewebservices.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) (limited to 'qt-ui/subsurfacewebservices.cpp') diff --git a/qt-ui/subsurfacewebservices.cpp b/qt-ui/subsurfacewebservices.cpp index 13a5a8a87..a336a4864 100644 --- a/qt-ui/subsurfacewebservices.cpp +++ b/qt-ui/subsurfacewebservices.cpp @@ -327,9 +327,11 @@ void SubsurfaceWebServices::buttonClicked(QAbstractButton* button) } break; case QDialogButtonBox::RejectRole: - // we may want to clean up after ourselves - // reply->deleteLater(); - reply = NULL; + if (reply != NULL && reply->isOpen()) { + reply->abort(); + delete reply; + reply = NULL; + } resetState(); break; case QDialogButtonBox::HelpRole: @@ -569,10 +571,15 @@ void DivelogsDeWebServices::uploadDives(QIODevice *dldContent) multipart = ∓ hideDownload(); + resetState(); exec(); - multipart = NULL; - delete reply; // we need to ensure it has stopped using our QHttpMultiPart + multipart = NULL; + if (reply != NULL && reply->isOpen()) { + reply->abort(); + delete reply; + reply = NULL; + } } DivelogsDeWebServices::DivelogsDeWebServices(QWidget* parent, Qt::WindowFlags f): WebServices(parent, f) @@ -755,6 +762,8 @@ void DivelogsDeWebServices::uploadFinished() // check what the server sent us: it might contain // an error condition, such as a failed login QByteArray xmlData = reply->readAll(); + reply->deleteLater(); + reply = NULL; char *resp = xmlData.data(); if (resp) { char *parsed = strstr(resp, ""); @@ -782,7 +791,7 @@ void DivelogsDeWebServices::setStatusText(int status) void DivelogsDeWebServices::downloadError(QNetworkReply::NetworkError) { resetState(); - ui.status->setText(tr("Download error: %1").arg(reply->errorString())); + ui.status->setText(tr("Error: %1").arg(reply->errorString())); reply->deleteLater(); reply = NULL; } @@ -820,7 +829,6 @@ void DivelogsDeWebServices::buttonClicked(QAbstractButton* button) case QDialogButtonBox::RejectRole: // these two seem to be causing a crash: // reply->deleteLater(); - // reply = NULL; resetState(); break; case QDialogButtonBox::HelpRole: -- cgit v1.2.3-70-g09d2