mirror of
https://github.com/GoldenCheetah/GoldenCheetah.git
synced 2026-02-15 00:49:55 +00:00
.. gets rid of an iritating compiler warning when compiling with VS2015 and MSVC .. is good because user metrics aren't fixed so the assert assumption is wrong now .. means we have to add assert.h to all the source files that had it from including RideMetric.h and want to use it.
510 lines
13 KiB
C++
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();
|
|
}
|
|
|
|
|
|
|