mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	Make sure 'changed' in SubsurfaceWebServices::buttonClicked() is not unused even if NO_MARBLE is defined. It appears to be used only in the #ifndef NO_MARBLE branch. Signed-off-by: Lubomir I. Ivanov <neolit123@gmail.com>
		
			
				
	
	
		
			1121 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1121 lines
		
	
	
	
		
			33 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "subsurfacewebservices.h"
 | 
						|
#include "helpers.h"
 | 
						|
#include "webservice.h"
 | 
						|
#include "mainwindow.h"
 | 
						|
#include "usersurvey.h"
 | 
						|
#include "divelist.h"
 | 
						|
#include "globe.h"
 | 
						|
#include "maintab.h"
 | 
						|
#include "display.h"
 | 
						|
#include "membuffer.h"
 | 
						|
#include <errno.h>
 | 
						|
 | 
						|
#include <QDir>
 | 
						|
#include <QHttpMultiPart>
 | 
						|
#include <QMessageBox>
 | 
						|
#include <QSettings>
 | 
						|
#include <QXmlStreamReader>
 | 
						|
#include <qdesktopservices.h>
 | 
						|
#include <QShortcut>
 | 
						|
#include <QDebug>
 | 
						|
 | 
						|
#ifdef Q_OS_UNIX
 | 
						|
#include <unistd.h> // for dup(2)
 | 
						|
#endif
 | 
						|
 | 
						|
#include <QUrlQuery>
 | 
						|
 | 
						|
#ifndef PATH_MAX
 | 
						|
#define PATH_MAX 4096
 | 
						|
#endif
 | 
						|
 | 
						|
struct dive_table gps_location_table;
 | 
						|
 | 
						|
// we don't overwrite any existing GPS info in the dive
 | 
						|
// so get the dive site and if there is none or there is one without GPS fix, add it
 | 
						|
static void copy_gps_location(struct dive *from, struct dive *to)
 | 
						|
{
 | 
						|
	struct dive_site *ds = get_dive_site_for_dive(to);
 | 
						|
	if (!ds || !dive_site_has_gps_location(ds)) {
 | 
						|
		struct dive_site *gds = get_dive_site_for_dive(from);
 | 
						|
		if (!ds) {
 | 
						|
			// simply link to the one created for the fake dive
 | 
						|
			to->dive_site_uuid = gds->uuid;
 | 
						|
		} else {
 | 
						|
			ds->latitude = gds->latitude;
 | 
						|
			ds->longitude = gds->longitude;
 | 
						|
			if (same_string(ds->name, ""))
 | 
						|
				ds->name = copy_string(gds->name);
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
#define SAME_GROUP 6 * 3600 // six hours
 | 
						|
//TODO: C Code. static functions are not good if we plan to have a test for them.
 | 
						|
static bool merge_locations_into_dives(void)
 | 
						|
{
 | 
						|
	int i, j, tracer=0, changed=0;
 | 
						|
	struct dive *gpsfix, *nextgpsfix, *dive;
 | 
						|
 | 
						|
	sort_table(&gps_location_table);
 | 
						|
 | 
						|
	for_each_dive (i, dive) {
 | 
						|
		if (!dive_has_gps_location(dive)) {
 | 
						|
			for (j = tracer; (gpsfix = get_dive_from_table(j, &gps_location_table)) !=NULL; j++) {
 | 
						|
				if (time_during_dive_with_offset(dive, gpsfix->when, SAME_GROUP)) {
 | 
						|
					if (verbose)
 | 
						|
						qDebug() << "processing gpsfix @" << get_dive_date_string(gpsfix->when) <<
 | 
						|
							    "which is withing six hours of dive from" <<
 | 
						|
							    get_dive_date_string(dive->when) << "until" <<
 | 
						|
							    get_dive_date_string(dive->when + dive->duration.seconds);
 | 
						|
					/*
 | 
						|
					 * If position is fixed during dive. This is the good one.
 | 
						|
					 * Asign and mark position, and end gps_location loop
 | 
						|
					 */
 | 
						|
					if (time_during_dive_with_offset(dive, gpsfix->when, 0)) {
 | 
						|
						if (verbose)
 | 
						|
							qDebug() << "gpsfix is during the dive, pick that one";
 | 
						|
						copy_gps_location(gpsfix, dive);
 | 
						|
						changed++;
 | 
						|
						tracer = j;
 | 
						|
						break;
 | 
						|
					} else {
 | 
						|
						/*
 | 
						|
						 * If it is not, check if there are more position fixes in SAME_GROUP range
 | 
						|
						 */
 | 
						|
						if ((nextgpsfix = get_dive_from_table(j + 1, &gps_location_table)) &&
 | 
						|
						    time_during_dive_with_offset(dive, nextgpsfix->when, SAME_GROUP)) {
 | 
						|
							if (verbose)
 | 
						|
								qDebug() << "look at the next gps fix @" << get_dive_date_string(nextgpsfix->when);
 | 
						|
							/* first let's test if this one is during the dive */
 | 
						|
							if (time_during_dive_with_offset(dive, nextgpsfix->when, 0)) {
 | 
						|
								if (verbose)
 | 
						|
									qDebug() << "which is during the dive, pick that one";
 | 
						|
								copy_gps_location(nextgpsfix, dive);
 | 
						|
								changed++;
 | 
						|
								tracer = j + 1;
 | 
						|
								break;
 | 
						|
							}
 | 
						|
							/* we know the gps fixes are sorted; if they are both before the dive, ignore the first,
 | 
						|
							 * if theay are both after the dive, take the first,
 | 
						|
							 * if the first is before and the second is after, take the closer one */
 | 
						|
							if (nextgpsfix->when < dive->when) {
 | 
						|
								if (verbose)
 | 
						|
									qDebug() << "which is closer to the start of the dive, do continue with that";
 | 
						|
								continue;
 | 
						|
							} else if (gpsfix->when > dive->when + dive->duration.seconds) {
 | 
						|
								if (verbose)
 | 
						|
									qDebug() << "which is even later after the end of the dive, so pick the previous one";
 | 
						|
								copy_gps_location(gpsfix, dive);
 | 
						|
								changed++;
 | 
						|
								tracer = j;
 | 
						|
								break;
 | 
						|
							} else {
 | 
						|
								/* ok, gpsfix is before, nextgpsfix is after */
 | 
						|
								if (dive->when - gpsfix->when <= nextgpsfix->when - (dive->when + dive->duration.seconds)) {
 | 
						|
									if (verbose)
 | 
						|
										qDebug() << "pick the one before as it's closer to the start";
 | 
						|
									copy_gps_location(gpsfix, dive);
 | 
						|
									changed++;
 | 
						|
									tracer = j;
 | 
						|
									break;
 | 
						|
								} else {
 | 
						|
									if (verbose)
 | 
						|
										qDebug() << "pick the one after as it's closer to the start";
 | 
						|
									copy_gps_location(nextgpsfix, dive);
 | 
						|
									changed++;
 | 
						|
									tracer = j + 1;
 | 
						|
									break;
 | 
						|
								}
 | 
						|
							}
 | 
						|
						/*
 | 
						|
						 * If no more positions in range, the actual is the one. Asign, mark and end loop.
 | 
						|
						 */
 | 
						|
						} else {
 | 
						|
							if (verbose)
 | 
						|
								qDebug() << "which seems to be the best one for this dive, so pick it";
 | 
						|
							copy_gps_location(gpsfix, dive);
 | 
						|
							changed++;
 | 
						|
							tracer = j;
 | 
						|
							break;
 | 
						|
						}
 | 
						|
					}
 | 
						|
				} else {
 | 
						|
					/* If position is out of SAME_GROUP range and in the future, mark position for
 | 
						|
					 * next dive iteration and end the gps_location loop
 | 
						|
					 */
 | 
						|
					if (gpsfix->when >= dive->when + dive->duration.seconds + SAME_GROUP) {
 | 
						|
						tracer = j;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return changed > 0;
 | 
						|
}
 | 
						|
 | 
						|
// TODO: This looks like should be ported to C code. or a big part of it.
 | 
						|
bool DivelogsDeWebServices::prepare_dives_for_divelogs(const QString &tempfile, const bool selected)
 | 
						|
{
 | 
						|
	static const char errPrefix[] = "divelog.de-upload:";
 | 
						|
	if (!amount_selected) {
 | 
						|
		report_error(tr("no dives were selected").toUtf8());
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	xsltStylesheetPtr xslt = NULL;
 | 
						|
	struct zip *zip;
 | 
						|
 | 
						|
	xslt = get_stylesheet("divelogs-export.xslt");
 | 
						|
	if (!xslt) {
 | 
						|
		qDebug() << errPrefix << "missing stylesheet";
 | 
						|
		report_error(tr("stylesheet to export to divelogs.de is not found").toUtf8());
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
 | 
						|
	int error_code;
 | 
						|
	zip = zip_open(QFile::encodeName(QDir::toNativeSeparators(tempfile)), ZIP_CREATE, &error_code);
 | 
						|
	if (!zip) {
 | 
						|
		char buffer[1024];
 | 
						|
		zip_error_to_str(buffer, sizeof buffer, error_code, errno);
 | 
						|
		report_error(tr("failed to create zip file for upload: %s").toUtf8(), buffer);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	/* walk the dive list in chronological order */
 | 
						|
	int i;
 | 
						|
	struct dive *dive;
 | 
						|
	for_each_dive (i, dive) {
 | 
						|
		char filename[PATH_MAX];
 | 
						|
		int streamsize;
 | 
						|
		const char *membuf;
 | 
						|
		xmlDoc *transformed;
 | 
						|
		struct zip_source *s;
 | 
						|
		struct membuffer mb = { 0 };
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Get the i'th dive in XML format so we can process it.
 | 
						|
		 * We need to save to a file before we can reload it back into memory...
 | 
						|
		 */
 | 
						|
		if (selected && !dive->selected)
 | 
						|
			continue;
 | 
						|
		/* make sure the buffer is empty and add the dive */
 | 
						|
		mb.len = 0;
 | 
						|
 | 
						|
		struct dive_site *ds = get_dive_site_by_uuid(dive->dive_site_uuid);
 | 
						|
 | 
						|
		if (ds) {
 | 
						|
			put_format(&mb, "<divelog><divesites><site uuid='%8x' name='", dive->dive_site_uuid);
 | 
						|
			put_quoted(&mb, ds->name, 1, 0);
 | 
						|
			put_format(&mb, "'");
 | 
						|
			if (ds->latitude.udeg || ds->longitude.udeg) {
 | 
						|
				put_degrees(&mb, ds->latitude, " gps='", " ");
 | 
						|
				put_degrees(&mb, ds->longitude, "", "'");
 | 
						|
			}
 | 
						|
			put_format(&mb, "/>\n</divesites>\n");
 | 
						|
		}
 | 
						|
 | 
						|
		save_one_dive_to_mb(&mb, dive);
 | 
						|
 | 
						|
		if (ds) {
 | 
						|
			put_format(&mb, "</divelog>\n");
 | 
						|
		}
 | 
						|
		membuf = mb_cstring(&mb);
 | 
						|
		streamsize = strlen(membuf);
 | 
						|
		/*
 | 
						|
		 * Parse the memory buffer into XML document and
 | 
						|
		 * transform it to divelogs.de format, finally dumping
 | 
						|
		 * the XML into a character buffer.
 | 
						|
		 */
 | 
						|
		xmlDoc *doc = xmlReadMemory(membuf, streamsize, "divelog", NULL, 0);
 | 
						|
		if (!doc) {
 | 
						|
			qWarning() << errPrefix << "could not parse back into memory the XML file we've just created!";
 | 
						|
			report_error(tr("internal error").toUtf8());
 | 
						|
			goto error_close_zip;
 | 
						|
		}
 | 
						|
		free((void *)membuf);
 | 
						|
 | 
						|
		transformed = xsltApplyStylesheet(xslt, doc, NULL);
 | 
						|
		if (!transformed) {
 | 
						|
			qWarning() << errPrefix << "XSLT transform failed for dive: " << i;
 | 
						|
			report_error(tr("Conversion of dive %1 to divelogs.de format failed").arg(i).toUtf8());
 | 
						|
			continue;
 | 
						|
		}
 | 
						|
		xmlDocDumpMemory(transformed, (xmlChar **)&membuf, &streamsize);
 | 
						|
		xmlFreeDoc(doc);
 | 
						|
		xmlFreeDoc(transformed);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Save the XML document into a zip file.
 | 
						|
		 */
 | 
						|
		snprintf(filename, PATH_MAX, "%d.xml", i + 1);
 | 
						|
		s = zip_source_buffer(zip, membuf, streamsize, 1);
 | 
						|
		if (s) {
 | 
						|
			int64_t ret = zip_add(zip, filename, s);
 | 
						|
			if (ret == -1)
 | 
						|
				qDebug() << errPrefix << "failed to include dive:" << i;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	xsltFreeStylesheet(xslt);
 | 
						|
	if (zip_close(zip)) {
 | 
						|
		int ze, se;
 | 
						|
#if LIBZIP_VERSION_MAJOR >= 1
 | 
						|
		zip_error_t *error = zip_get_error(zip);
 | 
						|
		ze = zip_error_code_zip(error);
 | 
						|
		se = zip_error_code_system(error);
 | 
						|
#else
 | 
						|
		zip_error_get(zip, &ze, &se);
 | 
						|
#endif
 | 
						|
		report_error(qPrintable(tr("error writing zip file: %s zip error %d system error %d - %s")),
 | 
						|
			     qPrintable(QDir::toNativeSeparators(tempfile)), ze, se, zip_strerror(zip));
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
 | 
						|
error_close_zip:
 | 
						|
	zip_close(zip);
 | 
						|
	QFile::remove(tempfile);
 | 
						|
	xsltFreeStylesheet(xslt);
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
	defaultApplyText = ui.buttonBox->button(QDialogButtonBox::Apply)->text();
 | 
						|
	userAgent = getUserAgent();
 | 
						|
}
 | 
						|
 | 
						|
void WebServices::hidePassword()
 | 
						|
{
 | 
						|
	ui.password->hide();
 | 
						|
	ui.passLabel->hide();
 | 
						|
}
 | 
						|
 | 
						|
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 == -1) {
 | 
						|
		total = INT_MAX / 2 - 1;
 | 
						|
	}
 | 
						|
	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("Transferring 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());
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setText(defaultApplyText);
 | 
						|
}
 | 
						|
 | 
						|
// #
 | 
						|
// #
 | 
						|
// #		Subsurface Web Service Implementation.
 | 
						|
// #
 | 
						|
// #
 | 
						|
 | 
						|
SubsurfaceWebServices::SubsurfaceWebServices(QWidget *parent, Qt::WindowFlags f) : WebServices(parent, f)
 | 
						|
{
 | 
						|
	QSettings s;
 | 
						|
	if (!prefs.save_userid_local || !*prefs.userid)
 | 
						|
		ui.userID->setText(s.value("subsurface_webservice_uid").toString().toUpper());
 | 
						|
	else
 | 
						|
		ui.userID->setText(prefs.userid);
 | 
						|
	hidePassword();
 | 
						|
	hideUpload();
 | 
						|
	ui.progressBar->setFormat(tr("Enter User ID and click Download"));
 | 
						|
	ui.progressBar->setRange(0, 1);
 | 
						|
	ui.progressBar->setValue(-1);
 | 
						|
	ui.progressBar->setAlignment(Qt::AlignCenter);
 | 
						|
	ui.saveUidLocal->setChecked(prefs.save_userid_local);
 | 
						|
	QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this);
 | 
						|
	connect(close, SIGNAL(activated()), this, SLOT(close()));
 | 
						|
	QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
 | 
						|
	connect(quit, SIGNAL(activated()), parent, SLOT(close()));
 | 
						|
}
 | 
						|
 | 
						|
void SubsurfaceWebServices::buttonClicked(QAbstractButton *button)
 | 
						|
{
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
 | 
						|
	switch (ui.buttonBox->buttonRole(button)) {
 | 
						|
	case QDialogButtonBox::ApplyRole: {
 | 
						|
		int i;
 | 
						|
		struct dive *d;
 | 
						|
		struct dive_site *ds;
 | 
						|
		bool changed = false;
 | 
						|
		(void)changed;
 | 
						|
		clear_table(&gps_location_table);
 | 
						|
		QByteArray url = tr("Webservice").toLocal8Bit();
 | 
						|
		parse_xml_buffer(url.data(), downloadedData.data(), downloadedData.length(), &gps_location_table, NULL);
 | 
						|
		// make sure we mark all the dive sites that were created
 | 
						|
		for (i = 0; i < gps_location_table.nr; i++) {
 | 
						|
			d = get_dive_from_table(i, &gps_location_table);
 | 
						|
			ds = get_dive_site_by_uuid(d->dive_site_uuid);
 | 
						|
			if (ds)
 | 
						|
				ds->notes = strdup("SubsurfaceWebservice");
 | 
						|
		}
 | 
						|
		/* now merge the data in the gps_location table into the dive_table */
 | 
						|
		if (merge_locations_into_dives()) {
 | 
						|
			changed = true;
 | 
						|
			mark_divelist_changed(true);
 | 
						|
			MainWindow::instance()->information()->updateDiveInfo();
 | 
						|
		}
 | 
						|
 | 
						|
		/* store last entered uid in config */
 | 
						|
		QSettings s;
 | 
						|
		QString qDialogUid = ui.userID->text().toUpper();
 | 
						|
		bool qSaveUid = ui.saveUidLocal->checkState();
 | 
						|
		set_save_userid_local(qSaveUid);
 | 
						|
		if (qSaveUid) {
 | 
						|
			QString qSettingUid = s.value("subsurface_webservice_uid").toString();
 | 
						|
			QString qFileUid = QString(prefs.userid);
 | 
						|
			bool s_eq_d = (qSettingUid == qDialogUid);
 | 
						|
			bool d_eq_f = (qDialogUid == qFileUid);
 | 
						|
			if (!d_eq_f || s_eq_d)
 | 
						|
				s.setValue("subsurface_webservice_uid", qDialogUid);
 | 
						|
			set_userid(qDialogUid.toLocal8Bit().data());
 | 
						|
		} else {
 | 
						|
			s.setValue("subsurface_webservice_uid", qDialogUid);
 | 
						|
		}
 | 
						|
		s.sync();
 | 
						|
		hide();
 | 
						|
		close();
 | 
						|
		resetState();
 | 
						|
		/* and now clean up and remove all the extra dive sites that were created */
 | 
						|
		QSet<uint32_t> usedUuids;
 | 
						|
		for_each_dive(i, d) {
 | 
						|
			if (d->dive_site_uuid)
 | 
						|
				usedUuids.insert(d->dive_site_uuid);
 | 
						|
		}
 | 
						|
		for_each_dive_site(i, ds) {
 | 
						|
			if (!usedUuids.contains(ds->uuid) && same_string(ds->notes, "SubsurfaceWebservice")) {
 | 
						|
				delete_dive_site(ds->uuid);
 | 
						|
				i--; // otherwise we skip one site
 | 
						|
			}
 | 
						|
		}
 | 
						|
#ifndef NO_MARBLE
 | 
						|
		// finally now that all the extra GPS fixes that weren't used have been deleted
 | 
						|
		// we can update the globe
 | 
						|
		if (changed) {
 | 
						|
			GlobeGPS::instance()->repopulateLabels();
 | 
						|
			GlobeGPS::instance()->centerOnDiveSite(get_dive_site_by_uuid(current_dive->dive_site_uuid));
 | 
						|
		}
 | 
						|
#endif
 | 
						|
 | 
						|
	} break;
 | 
						|
	case QDialogButtonBox::RejectRole:
 | 
						|
		if (reply != NULL && reply->isOpen()) {
 | 
						|
			reply->abort();
 | 
						|
			delete reply;
 | 
						|
			reply = NULL;
 | 
						|
		}
 | 
						|
		resetState();
 | 
						|
		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/");
 | 
						|
	QUrlQuery query;
 | 
						|
	query.addQueryItem("login", ui.userID->text().toUpper());
 | 
						|
	url.setQuery(query);
 | 
						|
 | 
						|
	QNetworkRequest request;
 | 
						|
	request.setUrl(url);
 | 
						|
	request.setRawHeader("Accept", "text/xml");
 | 
						|
	request.setRawHeader("User-Agent", userAgent.toUtf8());
 | 
						|
	reply = manager()->get(request);
 | 
						|
	ui.status->setText(tr("Connecting..."));
 | 
						|
	ui.progressBar->setEnabled(true);
 | 
						|
	ui.progressBar->setRange(0, 0); // this makes the progressbar do an 'infinite spin'
 | 
						|
	ui.download->setEnabled(false);
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
 | 
						|
	connectSignalsForDownload(reply);
 | 
						|
}
 | 
						|
 | 
						|
void SubsurfaceWebServices::downloadFinished()
 | 
						|
{
 | 
						|
	if (!reply)
 | 
						|
		return;
 | 
						|
 | 
						|
	ui.progressBar->setRange(0, 1);
 | 
						|
	ui.progressBar->setValue(1);
 | 
						|
	ui.progressBar->setFormat("%p%");
 | 
						|
	downloadedData = reply->readAll();
 | 
						|
 | 
						|
	ui.download->setEnabled(true);
 | 
						|
	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);
 | 
						|
	}
 | 
						|
	reply->deleteLater();
 | 
						|
	reply = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void SubsurfaceWebServices::downloadError(QNetworkReply::NetworkError)
 | 
						|
{
 | 
						|
	resetState();
 | 
						|
	ui.status->setText(tr("Download error: %1").arg(reply->errorString()));
 | 
						|
	reply->deleteLater();
 | 
						|
	reply = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void SubsurfaceWebServices::setStatusText(int status)
 | 
						|
{
 | 
						|
	QString text;
 | 
						|
	switch (status) {
 | 
						|
	case DD_STATUS_ERROR_CONNECT:
 | 
						|
		text = tr("Connection error: ");
 | 
						|
		break;
 | 
						|
	case DD_STATUS_ERROR_ID:
 | 
						|
		text = tr("Invalid user identifier!");
 | 
						|
		break;
 | 
						|
	case DD_STATUS_ERROR_PARSE:
 | 
						|
		text = tr("Cannot parse response!");
 | 
						|
		break;
 | 
						|
	case DD_STATUS_OK:
 | 
						|
		text = tr("Download successful");
 | 
						|
		break;
 | 
						|
	}
 | 
						|
	ui.status->setText(text);
 | 
						|
}
 | 
						|
 | 
						|
//TODO: C-Code.
 | 
						|
/* requires that there is a <download> or <error> tag under the <root> tag */
 | 
						|
void SubsurfaceWebServices::download_dialog_traverse_xml(xmlNodePtr node, unsigned int *download_status)
 | 
						|
{
 | 
						|
	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"))) {
 | 
						|
			*download_status = DD_STATUS_OK;
 | 
						|
			return;
 | 
						|
		} else if (!strcmp((const char *)cur_node->name, (const char *)"error")) {
 | 
						|
			*download_status = DD_STATUS_ERROR_ID;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// TODO: C-Code
 | 
						|
unsigned int SubsurfaceWebServices::download_dialog_parse_response(const QByteArray &xml)
 | 
						|
{
 | 
						|
	xmlNodePtr root;
 | 
						|
	xmlDocPtr doc = xmlParseMemory(xml.data(), xml.length());
 | 
						|
	unsigned int status = DD_STATUS_ERROR_PARSE;
 | 
						|
 | 
						|
	if (!doc)
 | 
						|
		return DD_STATUS_ERROR_PARSE;
 | 
						|
	root = xmlDocGetRootElement(doc);
 | 
						|
	if (!root) {
 | 
						|
		status = DD_STATUS_ERROR_PARSE;
 | 
						|
		goto end;
 | 
						|
	}
 | 
						|
	if (root->children)
 | 
						|
		download_dialog_traverse_xml(root->children, &status);
 | 
						|
end:
 | 
						|
	xmlFreeDoc(doc);
 | 
						|
	return status;
 | 
						|
}
 | 
						|
 | 
						|
// #
 | 
						|
// #
 | 
						|
// #		Divelogs DE  Web Service Implementation.
 | 
						|
// #
 | 
						|
// #
 | 
						|
 | 
						|
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 = QObject::tr("Invalid response from server");
 | 
						|
	bool seenDiveDates = false;
 | 
						|
	DiveListResult result;
 | 
						|
	result.idCount = 0;
 | 
						|
 | 
						|
	if (reader.readNextStartElement() && reader.name() != "DiveDateReader") {
 | 
						|
		result.errorCondition = invalidXmlError;
 | 
						|
		result.errorDetails =
 | 
						|
			QObject::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 = QObject::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 = QObject::tr("Malformed XML response. Line %1: %2")
 | 
						|
						.arg(reader.lineNumber())
 | 
						|
						.arg(reader.errorString());
 | 
						|
	}
 | 
						|
	return result;
 | 
						|
}
 | 
						|
 | 
						|
DivelogsDeWebServices *DivelogsDeWebServices::instance()
 | 
						|
{
 | 
						|
	static DivelogsDeWebServices *self = new DivelogsDeWebServices(MainWindow::instance());
 | 
						|
	self->setAttribute(Qt::WA_QuitOnClose, false);
 | 
						|
	return self;
 | 
						|
}
 | 
						|
 | 
						|
void DivelogsDeWebServices::downloadDives()
 | 
						|
{
 | 
						|
	uploadMode = false;
 | 
						|
	resetState();
 | 
						|
	hideUpload();
 | 
						|
	exec();
 | 
						|
}
 | 
						|
 | 
						|
void DivelogsDeWebServices::prepareDivesForUpload(bool selected)
 | 
						|
{
 | 
						|
	/* generate a random filename and create/open that file with zip_open */
 | 
						|
	QString filename = QDir::tempPath() + "/import-" + QString::number(qrand() % 99999999) + ".dld";
 | 
						|
	if (prepare_dives_for_divelogs(filename, selected)) {
 | 
						|
		QFile f(filename);
 | 
						|
		if (f.open(QIODevice::ReadOnly)) {
 | 
						|
			uploadDives((QIODevice *)&f);
 | 
						|
			f.close();
 | 
						|
			f.remove();
 | 
						|
			return;
 | 
						|
		} else {
 | 
						|
			report_error("Failed to open upload file %s\n", qPrintable(filename));
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		report_error("Failed to create upload file %s\n", qPrintable(filename));
 | 
						|
	}
 | 
						|
	MainWindow::instance()->getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
 | 
						|
}
 | 
						|
 | 
						|
void DivelogsDeWebServices::uploadDives(QIODevice *dldContent)
 | 
						|
{
 | 
						|
	QHttpMultiPart mp(QHttpMultiPart::FormDataType);
 | 
						|
	QHttpPart part;
 | 
						|
	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);
 | 
						|
 | 
						|
	multipart = ∓
 | 
						|
	hideDownload();
 | 
						|
	resetState();
 | 
						|
	uploadMode = true;
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(true);
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Done"));
 | 
						|
	exec();
 | 
						|
 | 
						|
	multipart = NULL;
 | 
						|
	if (reply != NULL && reply->isOpen()) {
 | 
						|
		reply->abort();
 | 
						|
		delete reply;
 | 
						|
		reply = NULL;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
DivelogsDeWebServices::DivelogsDeWebServices(QWidget *parent, Qt::WindowFlags f) : WebServices(parent, f),
 | 
						|
	multipart(NULL),
 | 
						|
	uploadMode(false)
 | 
						|
{
 | 
						|
	QSettings s;
 | 
						|
	ui.userID->setText(s.value("divelogde_user").toString());
 | 
						|
	ui.password->setText(s.value("divelogde_pass").toString());
 | 
						|
	ui.saveUidLocal->hide();
 | 
						|
	hideUpload();
 | 
						|
	QShortcut *close = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_W), this);
 | 
						|
	connect(close, SIGNAL(activated()), this, SLOT(close()));
 | 
						|
	QShortcut *quit = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q), this);
 | 
						|
	connect(quit, SIGNAL(activated()), parent, SLOT(close()));
 | 
						|
}
 | 
						|
 | 
						|
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);
 | 
						|
	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");
 | 
						|
	request.setRawHeader("User-Agent", userAgent.toUtf8());
 | 
						|
 | 
						|
	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.setRawHeader("User-Agent", userAgent.toUtf8());
 | 
						|
	request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
 | 
						|
 | 
						|
	QUrlQuery body;
 | 
						|
	body.addQueryItem("user", ui.userID->text());
 | 
						|
	body.addQueryItem("pass", ui.password->text());
 | 
						|
 | 
						|
	reply = manager()->post(request, body.query(QUrl::FullyEncoded).toLatin1());
 | 
						|
	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.setRawHeader("Accept", "application/zip, */*");
 | 
						|
	request.setRawHeader("User-Agent", userAgent.toUtf8());
 | 
						|
	request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
 | 
						|
 | 
						|
	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());
 | 
						|
	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);
 | 
						|
#if defined(Q_OS_UNIX) && defined(LIBZIP_VERSION_MAJOR)
 | 
						|
	int duppedfd = dup(zipFile.handle());
 | 
						|
	struct zip *zip = NULL;
 | 
						|
	if (duppedfd >= 0) {
 | 
						|
		zip = zip_fdopen(duppedfd, 0, &errorcode);
 | 
						|
		if (!zip)
 | 
						|
			::close(duppedfd);
 | 
						|
	} else {
 | 
						|
		QMessageBox::critical(this, tr("Problem with download"),
 | 
						|
				      tr("The archive could not be opened:\n"));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
#else
 | 
						|
	struct zip *zip = zip_open(QFile::encodeName(zipFile.fileName()), 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;
 | 
						|
	}
 | 
						|
	// now allow the user to cancel or accept
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
 | 
						|
 | 
						|
	zip_close(zip);
 | 
						|
	zipFile.close();
 | 
						|
#if defined(Q_OS_UNIX) && defined(LIBZIP_VERSION_MAJOR)
 | 
						|
	::close(duppedfd);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void DivelogsDeWebServices::uploadFinished()
 | 
						|
{
 | 
						|
	if (!reply)
 | 
						|
		return;
 | 
						|
 | 
						|
	ui.progressBar->setRange(0, 1);
 | 
						|
	ui.upload->setEnabled(true);
 | 
						|
	ui.userID->setEnabled(true);
 | 
						|
	ui.password->setEnabled(true);
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Cancel)->setEnabled(false);
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true);
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setText(tr("Done"));
 | 
						|
	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();
 | 
						|
	reply->deleteLater();
 | 
						|
	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::downloadError(QNetworkReply::NetworkError)
 | 
						|
{
 | 
						|
	resetState();
 | 
						|
	ui.status->setText(tr("Error: %1").arg(reply->errorString()));
 | 
						|
	reply->deleteLater();
 | 
						|
	reply = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void DivelogsDeWebServices::uploadError(QNetworkReply::NetworkError error)
 | 
						|
{
 | 
						|
	downloadError(error);
 | 
						|
}
 | 
						|
 | 
						|
void DivelogsDeWebServices::buttonClicked(QAbstractButton *button)
 | 
						|
{
 | 
						|
	ui.buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false);
 | 
						|
	switch (ui.buttonBox->buttonRole(button)) {
 | 
						|
	case QDialogButtonBox::ApplyRole: {
 | 
						|
		/* in 'uploadMode' button is called 'Done' and closes the dialog */
 | 
						|
		if (uploadMode) {
 | 
						|
			hide();
 | 
						|
			close();
 | 
						|
			resetState();
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		/* parse file and import dives */
 | 
						|
		parse_file(QFile::encodeName(zipFile.fileName()));
 | 
						|
		process_dives(true, false);
 | 
						|
		MainWindow::instance()->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();
 | 
						|
	} 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;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
UserSurveyServices::UserSurveyServices(QWidget *parent, Qt::WindowFlags f) : WebServices(parent, f)
 | 
						|
{
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
QNetworkReply* UserSurveyServices::sendSurvey(QString values)
 | 
						|
{
 | 
						|
	QNetworkRequest request;
 | 
						|
	request.setUrl(QString("http://subsurface-divelog.org/survey?%1").arg(values));
 | 
						|
	request.setRawHeader("Accept", "text/xml");
 | 
						|
	request.setRawHeader("User-Agent", userAgent.toUtf8());
 | 
						|
	reply = manager()->get(request);
 | 
						|
	return reply;
 | 
						|
}
 | 
						|
 | 
						|
CloudStorageAuthenticate::CloudStorageAuthenticate(QObject *parent) :
 | 
						|
	QObject(parent),
 | 
						|
	reply(NULL)
 | 
						|
{
 | 
						|
	userAgent = getUserAgent();
 | 
						|
}
 | 
						|
 | 
						|
#define CLOUDURL QString(prefs.cloud_base_url)
 | 
						|
#define CLOUDBACKENDSTORAGE CLOUDURL + "/storage"
 | 
						|
#define CLOUDBACKENDVERIFY CLOUDURL + "/verify"
 | 
						|
#define CLOUDBACKENDUPDATE CLOUDURL + "/update"
 | 
						|
 | 
						|
QNetworkReply* CloudStorageAuthenticate::backend(QString email, QString password, QString pin, QString newpasswd)
 | 
						|
{
 | 
						|
	QString payload(email + " " + password);
 | 
						|
	QUrl requestUrl;
 | 
						|
	if (pin == "" && newpasswd == "") {
 | 
						|
		requestUrl = QUrl(CLOUDBACKENDSTORAGE);
 | 
						|
	} else if (newpasswd != "") {
 | 
						|
		requestUrl = QUrl(CLOUDBACKENDUPDATE);
 | 
						|
		payload += " " + newpasswd;
 | 
						|
	} else {
 | 
						|
		requestUrl = QUrl(CLOUDBACKENDVERIFY);
 | 
						|
		payload += " " + pin;
 | 
						|
	}
 | 
						|
	QNetworkRequest *request = new QNetworkRequest(requestUrl);
 | 
						|
	request->setRawHeader("Accept", "text/xml, text/plain");
 | 
						|
	request->setRawHeader("User-Agent", userAgent.toUtf8());
 | 
						|
	request->setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
 | 
						|
	reply = WebServices::manager()->post(*request, qPrintable(payload));
 | 
						|
	connect(reply, SIGNAL(finished()), this, SLOT(uploadFinished()));
 | 
						|
	connect(reply, SIGNAL(sslErrors(QList<QSslError>)), this, SLOT(sslErrors(QList<QSslError>)));
 | 
						|
	connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
 | 
						|
		SLOT(uploadError(QNetworkReply::NetworkError)));
 | 
						|
	return reply;
 | 
						|
}
 | 
						|
 | 
						|
void CloudStorageAuthenticate::uploadFinished()
 | 
						|
{
 | 
						|
	static QString myLastError;
 | 
						|
 | 
						|
	QString cloudAuthReply(reply->readAll());
 | 
						|
	qDebug() << "Completed connection with cloud storage backend, response" << cloudAuthReply;
 | 
						|
	if (cloudAuthReply == "[VERIFIED]" || cloudAuthReply == "[OK]") {
 | 
						|
		prefs.cloud_verification_status = CS_VERIFIED;
 | 
						|
		NotificationWidget *nw = MainWindow::instance()->getNotificationWidget();
 | 
						|
		if (nw->getNotificationText() == myLastError)
 | 
						|
			nw->hideNotification();
 | 
						|
		myLastError.clear();
 | 
						|
	} else if (cloudAuthReply == "[VERIFY]") {
 | 
						|
		prefs.cloud_verification_status = CS_NEED_TO_VERIFY;
 | 
						|
	} else if (cloudAuthReply == "[PASSWDCHANGED]") {
 | 
						|
		free(prefs.cloud_storage_password);
 | 
						|
		prefs.cloud_storage_password = prefs.cloud_storage_newpassword;
 | 
						|
		prefs.cloud_storage_newpassword = NULL;
 | 
						|
		emit passwordChangeSuccessful();
 | 
						|
		return;
 | 
						|
	} else {
 | 
						|
		prefs.cloud_verification_status = CS_INCORRECT_USER_PASSWD;
 | 
						|
		myLastError = cloudAuthReply;
 | 
						|
		report_error("%s", qPrintable(cloudAuthReply));
 | 
						|
		MainWindow::instance()->getNotificationWidget()->showNotification(get_error_string(), KMessageWidget::Error);
 | 
						|
	}
 | 
						|
	emit finishedAuthenticate();
 | 
						|
}
 | 
						|
 | 
						|
void CloudStorageAuthenticate::uploadError(QNetworkReply::NetworkError error)
 | 
						|
{
 | 
						|
	qDebug() << "Received error response from cloud storage backend:" << reply->errorString();
 | 
						|
}
 | 
						|
 | 
						|
void CloudStorageAuthenticate::sslErrors(QList<QSslError> errorList)
 | 
						|
{
 | 
						|
	if (verbose) {
 | 
						|
		qDebug() << "Received error response trying to set up https connection with cloud storage backend:";
 | 
						|
		Q_FOREACH (QSslError err, errorList) {
 | 
						|
			qDebug() << err.errorString();
 | 
						|
		}
 | 
						|
	}
 | 
						|
	QSslConfiguration conf = reply->sslConfiguration();
 | 
						|
	QSslCertificate cert = conf.peerCertificate();
 | 
						|
	QByteArray hexDigest = cert.digest().toHex();
 | 
						|
	if (reply->url().toString().contains(prefs.cloud_base_url) &&
 | 
						|
	    hexDigest == "13ff44c62996cfa5cd69d6810675490e") {
 | 
						|
		if (verbose)
 | 
						|
			qDebug() << "Overriding SSL check as I recognize the certificate digest" << hexDigest;
 | 
						|
		reply->ignoreSslErrors();
 | 
						|
	} else {
 | 
						|
		if (verbose)
 | 
						|
			qDebug() << "got invalid SSL certificate with hex digest" << hexDigest;
 | 
						|
	}
 | 
						|
}
 |