mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	This is complete nonsense and should never have been merged. Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
		
			
				
	
	
		
			339 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			339 lines
		
	
	
	
		
			8.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| #include "uploadDiveLogsDE.h"
 | |
| #include <QDir>
 | |
| #include <QDebug>
 | |
| #include <zip.h>
 | |
| #include <errno.h>
 | |
| #include "core/display.h"
 | |
| #include "core/errorhelper.h"
 | |
| #include "core/qthelper.h"
 | |
| #include "core/dive.h"
 | |
| #include "core/membuffer.h"
 | |
| #include "core/divesite.h"
 | |
| #include "core/cloudstorage.h"
 | |
| #ifndef SUBSURFACE_MOBILE
 | |
| #include "core/selection.h"
 | |
| #endif // SUBSURFACE_MOBILE
 | |
| #include "core/settings/qPrefCloudStorage.h"
 | |
| 
 | |
| 
 | |
| uploadDiveLogsDE *uploadDiveLogsDE::instance()
 | |
| {
 | |
| 	static uploadDiveLogsDE *self = new uploadDiveLogsDE;
 | |
| 
 | |
| 	return self;
 | |
| }
 | |
| 
 | |
| 
 | |
| uploadDiveLogsDE::uploadDiveLogsDE():
 | |
| 	reply(NULL),
 | |
| 	multipart(NULL)
 | |
| {
 | |
| 	timeout.setSingleShot(true);
 | |
| }
 | |
| 
 | |
| 
 | |
| void uploadDiveLogsDE::doUpload(bool selected, const QString &userid, const QString &password)
 | |
| {
 | |
| 	QString err;
 | |
| 
 | |
| 
 | |
| 	/* generate a temporary filename and create/open that file with zip_open */
 | |
| 	QString filename(QDir::tempPath() + "/divelogsde-upload.dld");
 | |
| 
 | |
| 	// delete file if it exist
 | |
| 
 | |
| 	QFile f(filename);
 | |
| 	if (f.open(QIODevice::ReadOnly)) {
 | |
| 		f.close();
 | |
| 		f.remove();
 | |
| 	}
 | |
| 
 | |
| 	// Make zip file, with all dives, in divelogs.de format
 | |
| 	if (!prepareDives(filename, selected)) {
 | |
| 		emit uploadFinish(false, tr("Cannot prepare dives, none selected?"));
 | |
| 		timeout.stop();
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	// And upload it
 | |
| 	uploadDives(filename, userid, password);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool uploadDiveLogsDE::prepareDives(const QString &tempfile, bool selected)
 | |
| {
 | |
| 	static const char errPrefix[] = "divelog.de-upload:";
 | |
| 
 | |
| 	xsltStylesheetPtr xslt = NULL;
 | |
| 	struct zip *zip;
 | |
| 
 | |
| 	emit uploadStatus(tr("building zip file to upload"));
 | |
| 
 | |
| 	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;
 | |
| 	}
 | |
| 
 | |
| 	// Prepare zip file
 | |
| 	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 = {};
 | |
| 
 | |
| 		/*
 | |
| 		 * 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 = dive->dive_site;
 | |
| 
 | |
| 		if (ds) {
 | |
| 			put_format(&mb, "<divelog><divesites><site uuid='%8x' name='", ds->uuid);
 | |
| 			put_quoted(&mb, ds->name, 1, 0);
 | |
| 			put_format(&mb, "'");
 | |
| 			put_location(&mb, &ds->location, " gps='", "'");
 | |
| 			put_format(&mb, ">\n");
 | |
| 			if (ds->taxonomy.nr) {
 | |
| 				for (int j = 0; j < ds->taxonomy.nr; j++) {
 | |
| 					struct taxonomy *t = &ds->taxonomy.category[j];
 | |
| 					if (t->category != TC_NONE && t->category == prefs.geocoding.category[j] && t->value) {
 | |
| 						put_format(&mb, "  <geo cat='%d'", t->category);
 | |
| 						put_format(&mb, " origin='%d' value='", t->origin);
 | |
| 						put_quoted(&mb, t->value, 1, 0);
 | |
| 						put_format(&mb, "'/>\n");
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			put_format(&mb, "</site>\n</divesites>\n");
 | |
| 		}
 | |
| 
 | |
| 		save_one_dive_to_mb(&mb, dive, false);
 | |
| 
 | |
| 		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());
 | |
| 			zip_close(zip);
 | |
| 			QFile::remove(tempfile);
 | |
| 			xsltFreeStylesheet(xslt);
 | |
| 			return false;
 | |
| 		}
 | |
| 		free_buffer(&mb);
 | |
| 
 | |
| 		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;
 | |
| }
 | |
| 
 | |
| 
 | |
| void uploadDiveLogsDE::uploadDives(const QString &filename, const QString &userid, const QString &password)
 | |
| {
 | |
| 	QHttpPart part1, part2, part3;
 | |
| 	static QNetworkRequest request;
 | |
| 	QString args;
 | |
| 
 | |
| 	// Check if there is an earlier request open
 | |
| 	if (reply != NULL && reply->isOpen()) {
 | |
| 		reply->abort();
 | |
| 		delete reply;
 | |
| 		reply = NULL;
 | |
| 	}
 | |
| 	if (multipart != NULL) {
 | |
| 		delete multipart;
 | |
| 		multipart = NULL;
 | |
| 	}
 | |
| 	multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
 | |
| 
 | |
| 	emit uploadStatus(tr("Uploading dives"));
 | |
| 
 | |
| 	// prepare header with filename (of all dives) and pointer to file
 | |
| 	args = "form-data; name=\"userfile\"; filename=\"" + filename + "\"";
 | |
| 	part1.setRawHeader("Content-Disposition", args.toLatin1());
 | |
| 	QFile *f = new QFile(filename);
 | |
| 	if (!f->open(QIODevice::ReadOnly)) {
 | |
| 		qDebug() << "ERROR opening zip file: " << filename;
 | |
| 		return;
 | |
| 	}
 | |
| 	part1.setBodyDevice(f);
 | |
| 	multipart->append(part1);
 | |
| 
 | |
| 	// Add userid
 | |
| 	args = "form-data; name=\"user\"";
 | |
| 	part2.setRawHeader("Content-Disposition", args.toLatin1());
 | |
| 	part2.setBody(qPrefCloudStorage::divelogde_user().toUtf8());
 | |
| 	multipart->append(part2);
 | |
| 
 | |
| 	// Add password
 | |
| 	args = "form-data; name=\"pass\"";
 | |
| 	part3.setRawHeader("Content-Disposition", args.toLatin1());
 | |
| 	part3.setBody(qPrefCloudStorage::divelogde_pass().toUtf8());
 | |
| 	multipart->append(part3);
 | |
| 
 | |
| 	// Prepare network request
 | |
| 	request.setUrl(QUrl("https://divelogs.de/DivelogsDirectImport.php"));
 | |
| 	request.setRawHeader("Accept", "text/xml, application/xml");
 | |
| 	request.setRawHeader("User-Agent", getUserAgent().toUtf8());
 | |
| 
 | |
| 	// Execute async.
 | |
| 	reply = manager()->post(request, multipart);
 | |
| 
 | |
| 	// connect signals from upload process
 | |
| 	connect(reply, SIGNAL(finished()), this, SLOT(uploadFinishedSlot()));
 | |
| 	connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this,
 | |
| 		SLOT(uploadErrorSlot(QNetworkReply::NetworkError)));
 | |
| 	connect(reply, SIGNAL(uploadProgress(qint64, qint64)), this,
 | |
| 		SLOT(updateProgressSlot(qint64, qint64)));
 | |
| 	connect(&timeout, SIGNAL(timeout()), this, SLOT(uploadTimeoutSlot()));
 | |
| 
 | |
| 	timeout.start(30000); // 30s
 | |
| }
 | |
| 
 | |
| 
 | |
| void uploadDiveLogsDE::updateProgressSlot(qint64 current, qint64 total)
 | |
| {
 | |
| 	if (!reply)
 | |
| 		return;
 | |
| 
 | |
| 	if (total <= 0 || current <= 0)
 | |
| 		return;
 | |
| 
 | |
| 	// Calculate percentage as 0.x (values between 0.0 and 1.0)
 | |
| 	// And signal whoever wants to know
 | |
| 	qreal percentage = (float)current / (float)total;
 | |
| 	emit uploadProgress(percentage, 1.0);
 | |
| 
 | |
| 	// reset the timer: 30 seconds after we last got any data
 | |
| 	timeout.start();
 | |
| }
 | |
| 
 | |
| 
 | |
| void uploadDiveLogsDE::uploadFinishedSlot()
 | |
| {
 | |
| 	QString err;
 | |
| 
 | |
| 	if (!reply)
 | |
| 		return;
 | |
| 
 | |
| 	// 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>")) {
 | |
| 					report_error(tr("Upload failed").toUtf8());
 | |
| 					return;
 | |
| 				}
 | |
| 				timeout.stop();
 | |
| 				err = tr("Upload successful");
 | |
| 				emit uploadFinish(true, err);
 | |
| 				return;
 | |
| 			}
 | |
| 			timeout.stop();
 | |
| 			err = tr("Login failed");
 | |
| 			report_error(err.toUtf8());
 | |
| 			emit uploadFinish(false, err);
 | |
| 			return;
 | |
| 		}
 | |
| 		timeout.stop();
 | |
| 		err = tr("Cannot parse response");
 | |
| 		report_error(tr("Cannot parse response").toUtf8());
 | |
| 		emit uploadFinish(false, err);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| void uploadDiveLogsDE::uploadTimeoutSlot()
 | |
| {
 | |
| 	timeout.stop();
 | |
| 	if (reply) {
 | |
| 		reply->deleteLater();
 | |
| 		reply = NULL;
 | |
| 	}
 | |
| 	QString err(tr("divelogs.de not responding"));
 | |
| 	report_error(err.toUtf8());
 | |
| 	emit uploadFinish(false, err);
 | |
| }
 | |
| 
 | |
| 
 | |
| void uploadDiveLogsDE::uploadErrorSlot(QNetworkReply::NetworkError error)
 | |
| {
 | |
| 	timeout.stop();
 | |
| 	if (reply) {
 | |
| 		reply->deleteLater();
 | |
| 		reply = NULL;
 | |
| 	}
 | |
| 	QString err(tr("network error %1").arg(error));
 | |
| 	report_error(err.toUtf8());
 | |
| 	emit uploadFinish(false, err);
 | |
| }
 |