Merge branch 'web' of github.com:neolit123/subsurface

This commit is contained in:
Dirk Hohndel 2013-12-09 17:22:33 +01:00
commit 8ac58181f7
4 changed files with 186 additions and 20 deletions

View file

@ -8,6 +8,7 @@
#include "models.h" #include "models.h"
#include "modeldelegates.h" #include "modeldelegates.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "subsurfacewebservices.h"
#include "../display.h" #include "../display.h"
#include <QApplication> #include <QApplication>
#include <QHeaderView> #include <QHeaderView>
@ -726,6 +727,8 @@ void DiveListView::contextMenuEvent(QContextMenuEvent *event)
popup.addAction(tr("export As UDDF"), this, SLOT(exportSelectedDivesAsUDDF())); popup.addAction(tr("export As UDDF"), this, SLOT(exportSelectedDivesAsUDDF()));
popup.addAction(tr("shift times"), this, SLOT(shiftTimes())); popup.addAction(tr("shift times"), this, SLOT(shiftTimes()));
} }
if (d)
popup.addAction(tr("upload dive(s) to divelogs.de"), this, SLOT(uploadToDivelogsDE()));
// "collapse all" really closes all trips, // "collapse all" really closes all trips,
// "collapse" keeps the trip with the selected dive open // "collapse" keeps the trip with the selected dive open
QAction * actionTaken = popup.exec(event->globalPos()); QAction * actionTaken = popup.exec(event->globalPos());
@ -781,3 +784,8 @@ void DiveListView::shiftTimes()
{ {
ShiftTimesDialog::instance()->show(); ShiftTimesDialog::instance()->show();
} }
void DiveListView::uploadToDivelogsDE()
{
DivelogsDeWebServices::instance()->prepareDivesForUpload();
}

View file

@ -49,6 +49,7 @@ public slots:
void saveSelectedDivesAs(); void saveSelectedDivesAs();
void exportSelectedDivesAsUDDF(); void exportSelectedDivesAsUDDF();
void shiftTimes(); void shiftTimes();
void uploadToDivelogsDE();
signals: signals:
void currentDiveChanged(int divenr); void currentDiveChanged(int divenr);

View file

@ -1,7 +1,6 @@
#include "subsurfacewebservices.h" #include "subsurfacewebservices.h"
#include "../webservice.h" #include "../webservice.h"
#include "mainwindow.h" #include "mainwindow.h"
#include <libxml/parser.h> #include <libxml/parser.h>
#include <zip.h> #include <zip.h>
#include <errno.h> #include <errno.h>
@ -99,6 +98,97 @@ static void clear_table(struct dive_table *table)
table->nr = 0; 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;
/* 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) {
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++) {
dive = get_dive(i);
if (!dive)
continue;
if (selected && !dive->selected)
continue;
f = tmpfile();
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)) {
fprintf(stderr, "divelog.de-upload: memory error\n");
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) {
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) {
fprintf(stderr, "divelog.de-upload: missing stylesheet\n");
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, "divelog.de-upload: failed to include dive %d\n", i);
}
}
zip_close(zip);
/* let's call this again */
tempfile = tempfileQ.toLocal8Bit().data();
return tempfile;
}
WebServices::WebServices(QWidget* parent, Qt::WindowFlags f): QDialog(parent, f) WebServices::WebServices(QWidget* parent, Qt::WindowFlags f): QDialog(parent, f)
, reply(0) , reply(0)
{ {
@ -237,9 +327,11 @@ void SubsurfaceWebServices::buttonClicked(QAbstractButton* button)
} }
break; break;
case QDialogButtonBox::RejectRole: case QDialogButtonBox::RejectRole:
// we may want to clean up after ourselves if (reply != NULL && reply->isOpen()) {
// reply->deleteLater(); reply->abort();
delete reply;
reply = NULL; reply = NULL;
}
resetState(); resetState();
break; break;
case QDialogButtonBox::HelpRole: case QDialogButtonBox::HelpRole:
@ -447,20 +539,47 @@ void DivelogsDeWebServices::downloadDives()
exec(); exec();
} }
void DivelogsDeWebServices::prepareDivesForUpload()
{
QString errorText(tr("Cannot create DLD file"));
char *filename = prepare_dives_for_divelogs(true);
if (filename) {
QFile f(filename);
if (f.exists()) {
f.open(QIODevice::ReadOnly);
uploadDives((QIODevice *)&f);
f.close();
f.remove();
return;
}
mainWindow()->showError(errorText.append(": ").append(filename));
return;
}
mainWindow()->showError(errorText.append("!"));
}
void DivelogsDeWebServices::uploadDives(QIODevice *dldContent) void DivelogsDeWebServices::uploadDives(QIODevice *dldContent)
{ {
QHttpMultiPart mp(QHttpMultiPart::FormDataType); QHttpMultiPart mp(QHttpMultiPart::FormDataType);
QHttpPart part; 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); part.setBodyDevice(dldContent);
mp.append(part); mp.append(part);
multipart = &mp; multipart = &mp;
hideDownload(); hideDownload();
resetState();
exec(); 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) DivelogsDeWebServices::DivelogsDeWebServices(QWidget* parent, Qt::WindowFlags f): WebServices(parent, f)
@ -473,6 +592,11 @@ DivelogsDeWebServices::DivelogsDeWebServices(QWidget* parent, Qt::WindowFlags f)
void DivelogsDeWebServices::startUpload() 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.status->setText(tr("Uploading dive list..."));
ui.progressBar->setRange(0,0); // this makes the progressbar do an 'infinite spin' ui.progressBar->setRange(0,0); // this makes the progressbar do an 'infinite spin'
ui.upload->setEnabled(false); ui.upload->setEnabled(false);
@ -638,8 +762,25 @@ void DivelogsDeWebServices::uploadFinished()
// check what the server sent us: it might contain // check what the server sent us: it might contain
// an error condition, such as a failed login // an error condition, such as a failed login
QByteArray xmlData = reply->readAll(); QByteArray xmlData = reply->readAll();
reply->deleteLater();
// ### FIXME: what's the format? reply = NULL;
char *resp = xmlData.data();
if (resp) {
char *parsed = strstr(resp, "<Login>");
if (parsed) {
if (strstr(resp, "<Login>succeeded</Login>")) {
if (strstr(resp, "<FileCopy>failed</FileCopy>")) {
ui.status->setText(tr("Upload failed"));
return;
}
ui.status->setText(tr("Upload successful"));
return;
}
ui.status->setText(tr("Login failed"));
return;
}
ui.status->setText(tr("Cannot parse response"));
}
} }
void DivelogsDeWebServices::setStatusText(int status) void DivelogsDeWebServices::setStatusText(int status)
@ -650,7 +791,7 @@ void DivelogsDeWebServices::setStatusText(int status)
void DivelogsDeWebServices::downloadError(QNetworkReply::NetworkError) void DivelogsDeWebServices::downloadError(QNetworkReply::NetworkError)
{ {
resetState(); resetState();
ui.status->setText(tr("Download error: %1").arg(reply->errorString())); ui.status->setText(tr("Error: %1").arg(reply->errorString()));
reply->deleteLater(); reply->deleteLater();
reply = NULL; reply = NULL;
} }
@ -663,22 +804,37 @@ void DivelogsDeWebServices::uploadError(QNetworkReply::NetworkError error)
void DivelogsDeWebServices::buttonClicked(QAbstractButton* button) void DivelogsDeWebServices::buttonClicked(QAbstractButton* button)
{ {
ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
switch(ui.buttonBox->buttonRole(button)){ switch(ui.buttonBox->buttonRole(button)){
case QDialogButtonBox::ApplyRole:{ case QDialogButtonBox::ApplyRole:{
char *errorptr = NULL; /* parse file and import dives */
parse_file(zipFile.fileName().toUtf8().constData(), &errorptr); char *error = NULL;
parse_file(zipFile.fileName().toLocal8Bit().data(), &error);
if (error != NULL) {
mainWindow()->showError(error);
free(error);
}
process_dives(TRUE, FALSE); process_dives(TRUE, FALSE);
// ### FIXME: do something useful with the error - but there shouldn't be one, right? mainWindow()->refreshDisplay();
if (errorptr)
qDebug() << errorptr;
/* 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(); hide();
close(); close();
resetState(); resetState();
mark_divelist_changed(TRUE);
mainWindow()->refreshDisplay();
} }
break;
case QDialogButtonBox::RejectRole:
// these two seem to be causing a crash:
// reply->deleteLater();
resetState();
break;
case QDialogButtonBox::HelpRole:
QDesktopServices::openUrl(QUrl("http://divelogs.de"));
break;
default:
break;
} }
} }

View file

@ -66,7 +66,7 @@ class DivelogsDeWebServices : public WebServices {
public: public:
static DivelogsDeWebServices * instance(); static DivelogsDeWebServices * instance();
void downloadDives(); void downloadDives();
void uploadDives(QIODevice *dldContent); void prepareDivesForUpload();
private slots: private slots:
void startDownload(); void startDownload();
@ -79,6 +79,7 @@ private slots:
void uploadError(QNetworkReply::NetworkError error); void uploadError(QNetworkReply::NetworkError error);
void startUpload(); void startUpload();
private: private:
void uploadDives(QIODevice *dldContent);
explicit DivelogsDeWebServices (QWidget* parent = 0, Qt::WindowFlags f = 0); explicit DivelogsDeWebServices (QWidget* parent = 0, Qt::WindowFlags f = 0);
void setStatusText(int status); void setStatusText(int status);
void download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status); void download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status);