Files
GoldenCheetah/deprecated/TrainingstagebuchUploader.cpp
Mark Liversedge f645df8040 Deprecate ShareDialog and Friends
.. they are now replaced via CloudService.
2017-04-24 20:32:37 +01:00

510 lines
13 KiB
C++

/*
* Copyright (c) 2012 Rainer Clasen <bj@zuto.de>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "TrainingstagebuchUploader.h"
#include "ShareDialog.h"
#include "Athlete.h"
#include "Settings.h"
#include "TimeUtils.h"
#include "Units.h"
#include <stdio.h>
#include <assert.h>
// access to metrics
#include "PwxRideFile.h"
/*
* - reuse single QNetworkAccessManager for all requests to allow
* connection re-use.
*
* - replies must be handled differently - based on request, but there's
* just a single mgr.finish() handler.
*
* - can't connect() to QNetworkReply::finish, as there's a
* slight chance for a race condition: request finished between call to
* mgr.post() and connect(). Therefore have to connect(QNetworkAccessManager::finish)
*
* - can't subclass QNetworkRequest and add attribute, as
* QNetworkAccessManager only takes a reference and clones a plain
* QNetworkRequest from it.
*
* - so... hack around this with currentRequest + dispatchRequest TODO: fix
*/
const QString TTB_URL( "http://trainingstagebuch.org" );
class TtbParser : public QXmlDefaultHandler
{
public:
friend class TrainingstagebuchUploader;
bool startElement( const QString&, const QString&, const QString&,
const QXmlAttributes& )
{
cdata = "";
return true;
};
bool endElement( const QString&, const QString&, const QString& qName )
{
if( qName == "error" ){
error = cdata;
return true;
}
return true;
}
bool characters( const QString& str)
{
cdata += str;
return true;
};
protected:
QString cdata;
QString error;
};
TrainingstagebuchUploader::TrainingstagebuchUploader(Context *context, RideItem *ride, ShareDialog *parent ) :
ShareDialogUploader( tr("Trainingstagebuch.org"), context, ride, parent),
proMember( false )
{
exerciseId = ride->ride()->getTag("TtbExercise", "");
connect(&networkMgr, SIGNAL(finished(QNetworkReply*)), this,
SLOT(dispatchReply(QNetworkReply*)));
}
bool
TrainingstagebuchUploader::canUpload( QString &err )
{
QString username = appsettings->cvalue(context->athlete->cyclist, GC_TTBUSER).toString();
if( username.length() > 0 ){
return true;
}
err = tr("Cannot upload to Trainingstagebuch without credentials. Check Settings");
return false;
}
bool
TrainingstagebuchUploader::wasUploaded()
{
return exerciseId.length() > 0;
}
void
TrainingstagebuchUploader::upload()
{
uploadSuccessful = false;
requestSettings();
if( !uploadSuccessful ){
parent->progressLabel->setText(tr("Error uploading to Trainingstagebuch.org"));
} else {
parent->progressLabel->setText(tr("successfully uploaded to Trainingstagebuch.org as %1")
.arg(exerciseId));
ride->ride()->setTag("TtbExercise", exerciseId );
ride->setDirty(true);
}
}
void
TrainingstagebuchUploader::requestSettings()
{
parent->progressLabel->setText(tr("getting Settings from Trainingstagebuch.org..."));
currentRequest = reqSettings;
QString username = appsettings->cvalue(context->athlete->cyclist, GC_TTBUSER).toString();
QString password = appsettings->cvalue(context->athlete->cyclist, GC_TTBPASS).toString();
#if QT_VERSION > 0x050000
QUrlQuery urlquery;
#else
QUrl urlquery( TTB_URL + "/settings/list" );
#endif
urlquery.addQueryItem( "view", "xml" );
urlquery.addQueryItem( "user", username );
urlquery.addQueryItem( "pass", password );
#if QT_VERSION > 0x050000
QUrl url (TTB_URL + "/settings/list");
url.setQuery(urlquery.query());
QNetworkRequest request = QNetworkRequest(url);
#else
QNetworkRequest request = QNetworkRequest(urlquery);
#endif
request.setRawHeader( "Accept-Encoding", "identity" );
request.setRawHeader( "Accept", "application/xml" );
request.setRawHeader( "Accept-Charset", "utf-8" );
networkMgr.get( request );
parent->progressBar->setValue(parent->progressBar->value()+5/parent->shareSiteCount);
eventLoop.exec();
}
void
TrainingstagebuchUploader::requestSession()
{
parent->progressLabel->setText(tr("getting new Trainingstagebuch.org Session..."));
currentRequest = reqSession;
QString username = appsettings->cvalue(context->athlete->cyclist, GC_TTBUSER).toString();
QString password = appsettings->cvalue(context->athlete->cyclist, GC_TTBPASS).toString();
#if QT_VERSION > 0x050000
QUrlQuery urlquery;
#else
QUrl urlquery( TTB_URL + "/login/sso" );
#endif
urlquery.addQueryItem( "view", "xml" );
urlquery.addQueryItem( "user", username );
urlquery.addQueryItem( "pass", password );
#if QT_VERSION > 0x050000
QUrl url (TTB_URL + "/login/sso");
url.setQuery(urlquery.query());
QNetworkRequest request = QNetworkRequest(url);
#else
QNetworkRequest request = QNetworkRequest(urlquery);
#endif
request.setRawHeader( "Accept-Encoding", "identity" );
request.setRawHeader( "Accept", "application/xml" );
request.setRawHeader( "Accept-Charset", "utf-8" );
networkMgr.get( request );
parent->progressBar->setValue(parent->progressBar->value()+5/parent->shareSiteCount);
}
void
TrainingstagebuchUploader::requestUpload()
{
assert(sessionId.length() > 0 );
parent->progressLabel->setText(tr("preparing Trainingstagebuch.org data ..."));
QHttpMultiPart *body = new QHttpMultiPart( QHttpMultiPart::FormDataType );
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"upload_submit\""));
textPart.setBody("hrm");
body->append( textPart );
QString fname = context->athlete->home->temp().absoluteFilePath(".ttbupload.pwx" );
QFile *uploadFile = new QFile( fname );
uploadFile->setParent(body);
PwxFileReader reader;
reader.writeRideFile(context, ride->ride(), *uploadFile );
parent->progressBar->setValue(parent->progressBar->value()+20/parent->shareSiteCount);
int limit = proMember
? 8 * 1024 * 1024
: 4 * 1024 * 1024;
if( uploadFile->size() >= limit ){
parent->errorLabel->setText(tr("temporary file too large for upload: %1 > %1 bytes")
.arg(uploadFile->size())
.arg(limit) );
eventLoop.quit();
return;
}
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader,
QVariant("application/octet-stream"));
filePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"file\"; filename=\"gc-upload-ttb.pwx\""));
uploadFile->open(QIODevice::ReadOnly);
filePart.setBodyDevice(uploadFile);
body->append( filePart );
parent->progressLabel->setText(tr("sending to Trainingstagebuch.org ..."));
currentRequest = reqUpload;
#if QT_VERSION > 0x050000
QUrlQuery urlquery;
#else
QUrl urlquery( TTB_URL + "/file/upload" );
#endif
urlquery.addQueryItem( "view", "xml" );
urlquery.addQueryItem( "sso", sessionId );
#if QT_VERSION > 0x050000
QUrl url (TTB_URL + "/file/upload");
url.setQuery(urlquery.query());
QNetworkRequest request = QNetworkRequest(url);
#else
QNetworkRequest request = QNetworkRequest(urlquery);
#endif
request.setRawHeader( "Accept-Encoding", "identity" );
request.setRawHeader( "Accept", "application/xml" );
request.setRawHeader( "Accept-Charset", "utf-8" );
QNetworkReply *reply = networkMgr.post( request, body );
body->setParent( reply );
}
void
TrainingstagebuchUploader::dispatchReply( QNetworkReply *reply )
{
if( reply->error() != QNetworkReply::NoError ){
parent->errorLabel->setText( tr("request failed: ")
+ reply->errorString() );
eventLoop.quit();
return;
}
QVariant status = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute );
if( ! status.isValid() || status.toInt() != 200 ){
QVariant msg( reply->attribute( QNetworkRequest::HttpReasonPhraseAttribute ) );
parent->errorLabel->setText( tr("request failed, Server response: %1 %2")
.arg(status.toInt())
.arg(msg.toString()) );
eventLoop.quit();
return;
}
switch( currentRequest ){
case reqSettings:
finishSettings( reply );
break;
case reqSession:
finishSession( reply );
break;
case reqUpload:
finishUpload( reply );
break;
}
}
class TtbSettingsParser : public TtbParser
{
public:
friend class TrainingstagebuchUploader;
TtbSettingsParser() :
pro(false),
reFalse("\\s*(0|false|no|)\\s*",Qt::CaseInsensitive )
{};
bool endElement( const QString& a, const QString&b, const QString& qName )
{
if( qName == "pro" ){
pro = ! reFalse.exactMatch(cdata);
return true;
} else if( qName == "session" ){
session = cdata;
return true;
}
return TtbParser::endElement( a, b, qName );
};
protected:
bool pro;
QString session;
QRegExp reFalse;
};
void
TrainingstagebuchUploader::finishSettings(QNetworkReply *reply)
{
parent->progressBar->setValue(parent->progressBar->value()+5/parent->shareSiteCount);
TtbSettingsParser handler;
QXmlInputSource source( reply );
QXmlSimpleReader reader;
reader.setContentHandler( &handler );
if( ! reader.parse( source ) ){
parent->errorLabel->setText(tr("failed to parse Settings response: ")
+handler.errorString());
eventLoop.quit();
return;
}
if( handler.error.length() > 0 ){
parent->errorLabel->setText(tr("failed to get settings: ")
+handler.error );
eventLoop.quit();
return;
}
sessionId = handler.session;
proMember = handler.pro;
if( sessionId.length() == 0 ){
requestSession();
} else {
requestUpload();
}
}
class TtbSessionParser : public TtbParser
{
public:
friend class TrainingstagebuchUploader;
bool endElement( const QString& a, const QString&b, const QString& qName )
{
if( qName == "session" ){
session = cdata;
return true;
}
return TtbParser::endElement( a, b, qName );
};
protected:
QString session;
};
void
TrainingstagebuchUploader::finishSession(QNetworkReply *reply)
{
parent->progressBar->setValue(parent->progressBar->value()+5/parent->shareSiteCount);
TtbSessionParser handler;
QXmlInputSource source( reply );
QXmlSimpleReader reader;
reader.setContentHandler( &handler );
if( ! reader.parse( source ) ){
parent->errorLabel->setText(tr("failed to parse Session response: ")
+handler.errorString());
eventLoop.quit();
return;
}
if( handler.error.length() > 0 ){
parent->errorLabel->setText(tr("failed to get new session: ")
+handler.error );
eventLoop.quit();
return;
}
sessionId = handler.session;
if( sessionId.length() == 0 ){
parent->errorLabel->setText(tr("got empty session"));
eventLoop.quit();
return;
}
requestUpload();
}
class TtbUploadParser : public TtbParser
{
public:
friend class TrainingstagebuchUploader;
bool endElement( const QString& a, const QString&b, const QString& qName )
{
if( qName == "id" ){
id = cdata;
return true;
}
return TtbParser::endElement( a, b, qName );
};
protected:
QString id;
};
void
TrainingstagebuchUploader::finishUpload(QNetworkReply *reply)
{
parent->progressBar->setValue(parent->progressBar->value()+60/parent->shareSiteCount);
TtbUploadParser handler;
QXmlInputSource source( reply );
QXmlSimpleReader reader;
reader.setContentHandler( &handler );
if( ! reader.parse( source ) ){
parent->errorLabel->setText(tr("failed to parse upload response: ")
+handler.errorString());
eventLoop.quit();
return;
}
if( handler.error.length() > 0 ){
parent->errorLabel->setText(tr("failed to upload file: ")
+handler.error );
eventLoop.quit();
return;
}
exerciseId = handler.id;
if( exerciseId.length() == 0 ){
parent->errorLabel->setText(tr("got empty exercise"));
eventLoop.quit();
return;
}
uploadSuccessful = true;
eventLoop.quit();
}