3
* $Id: clouduploadcommand.cpp $
6
* Copyright 2008-12 MetaEmotion S.L. All rights reserved.
7
* http://ginkgo-cadx.com
9
* This file is licensed under LGPL v3 license.
10
* See License.txt for details
16
#include <curl/curl.h>
20
#include "cloudcommand.h"
21
#include "comandoincluirhistorial.h"
22
#include <api/cloud.h>
23
#include <api/ientorno.h>
24
#include <api/controllers/ipacscontroller.h>
25
#include <api/controllers/icontroladorlog.h>
26
#include <api/controllers/icommandcontroller.h>
27
#include <api/controllers/ieventscontroller.h>
28
#include <eventos/mensajes.h>
29
#include <api/internationalization/internationalization.h>
31
#include <json/json.h>
32
#include <openssl/bio.h>
33
#include <openssl/evp.h>
36
#include <wx/filename.h>
37
#include <wx/tarstrm.h>
38
#include <wx/zstream.h>
39
#include <wx/wfstream.h>
40
#include <wx/wxhttpengine/httpbuilder.h>
42
#define CLOUD_PROTOCOL_VERSION "1.1"
44
inline std::string FromBase64(const std::string& input)
46
char* pOutput = (char*)malloc(input.size());
49
b64 = BIO_new(BIO_f_base64());
50
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
51
bin = BIO_new_mem_buf((void*) input.c_str(), input.size());
53
bin = BIO_push(b64, bin);
55
int size = BIO_read(bin, pOutput, input.size());
56
std::string outputstr(pOutput, size);
63
int progress_func_download_cloud(void* ptr, double TotalToDownload, double NowDownloaded, double , double )
65
GNC::GCS::CloudCommand* pCmd = (GNC::GCS::CloudCommand*)(ptr);
66
if (pCmd != NULL && TotalToDownload != 0) {
67
if (pCmd->NotificarProgreso((float)NowDownloaded/TotalToDownload, _Std("Downloading files..."))) {
80
struct InMemmoryDataHandler {
87
size_t static inMemmoryResponseHandler( void *ptr, size_t size, size_t nmemb, void *userdata)
89
size_t nbytes = size*nmemb;
90
struct InMemmoryDataHandler* dataHandler = (struct InMemmoryDataHandler*) userdata;
92
if (!(dataHandler)->initialized){
93
(dataHandler)->data = (char *)malloc(nbytes);
94
(dataHandler)->bufferlen = nbytes;
95
(dataHandler)->writepos = 0;
96
(dataHandler)->initialized = true;
99
if ((dataHandler)->bufferlen < ((dataHandler)->writepos + nbytes)){
100
(dataHandler)->bufferlen = (dataHandler)->bufferlen + nbytes;
101
(dataHandler)->data = (char*) realloc((dataHandler)->data, (size_t) ((dataHandler)->writepos + nbytes));
104
assert((dataHandler)->data != NULL);
105
memcpy( (dataHandler)->data + (dataHandler)->writepos, ptr, nbytes);
106
(dataHandler)->writepos += nbytes;
110
struct FileDataHandler {
111
wxFFileOutputStream* fos;
115
size_t static fileResponseHandler( void *ptr, size_t size, size_t nmemb, void *userdata)
117
size_t nbytes = size * nmemb;
118
struct FileDataHandler* dataHandler = (struct FileDataHandler *) userdata;
119
dataHandler->fos->Write(ptr, nbytes);
120
dataHandler->filelen += nbytes;
125
// CloudCommandParams
127
GNC::GCS::CloudCommandParams::CloudCommandParams(GNC::GCS::CloudCommandParams::ActionType action) : m_action(action)
131
GNC::GCS::CloudCommandParams::~CloudCommandParams()
135
// CloudGetCommandParams
137
GNC::GCS::CloudGetCommandParams::CloudGetCommandParams(const std::string& url, bool base64) : CloudCommandParams(CloudCommandParams::CC_GET), m_url (url)
140
std::string urlWithoutPrefix = url;
141
wxString urlWx = wxString::FromUTF8(url.c_str());
142
if (urlWx.StartsWith(wxT("ginkgocadx://cloud:"))) {
143
urlWithoutPrefix = urlWx.SubString(19,urlWx.size()-1).ToUTF8();
145
m_url = FromBase64(urlWithoutPrefix);
151
GNC::GCS::CloudGetCommandParams::~CloudGetCommandParams()
155
// CloudFindCommandParams
157
GNC::GCS::CloudFindCommandParams::CloudFindCommandParams(const std::string& bucketId,const std::string& userNameQuery, const std::string& descriptionQuery, const std::string& modalitiesQuery, const std::string& fromDateQuery, const std::string& toDateQuery) : CloudCommandParams(CloudCommandParams::CC_FIND), m_bucketId(bucketId), m_userNameQuery(userNameQuery), m_descriptionQuery(descriptionQuery), m_modalitiesQuery(modalitiesQuery), m_fromDateQuery(fromDateQuery), m_toDateQuery(toDateQuery)
161
GNC::GCS::CloudFindCommandParams::~CloudFindCommandParams()
167
GNC::GCS::CloudCommand::CloudCommand(::GNC::GCS::CloudCommandParams* pParams): GNC::GCS::IComando(pParams, _Std("CloudCommand"))
169
m_pCloudParams = pParams;
172
void GNC::GCS::CloudCommand::Execute()
176
if (m_pCloudParams->m_action == CloudCommandParams::CC_FIND) {
177
doFind(static_cast<CloudFindCommandParams*>(m_pParams));
179
else if (m_pCloudParams->m_action == CloudCommandParams::CC_GET) {
180
doGet(static_cast<CloudGetCommandParams*>(m_pParams));
183
m_pCloudParams->m_error = _Std("Unknown operation");
186
catch (std::exception& ex) {
187
std::ostringstream ostr;
188
ostr << _Std("Error in cloud operation. See log to get more details...") << std::endl;
190
m_pCloudParams->m_error = ostr.str();
194
void GNC::GCS::CloudCommand::doGet(GNC::GCS::CloudGetCommandParams* pParams) {
196
// Step 1: Setup handler
197
FileDataHandler responseDataHandler;
198
memset(&responseDataHandler, 0, sizeof(FileDataHandler));
200
std::ostringstream ostr;
202
ostr << GNC::GCS::IEntorno::Instance()->CreateGinkgoTempFile() << time(NULL) << ".tgz";
203
std::string tgzFile = ostr.str();
205
wxString tmpFile(FROMPATH(tgzFile));
207
responseDataHandler.fos = new wxFFileOutputStream(tmpFile);
213
double speed_upload, total_time;
215
curl = curl_easy_init();
217
curl_easy_setopt(curl, CURLOPT_URL, pParams->m_url.c_str());
219
std::ostringstream userAgent;
220
userAgent << "Mozilla/5.0 (compatible; GinkgoCADx " << GNC::GCS::IEntorno::Instance()->GetGinkgoVersionString() << " )";
221
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.str().c_str());
224
wxProxySettings settings;
225
settings.ProxySettingsLoadGeneral();
226
if (settings.m_bUseProxy) {
227
curl_easy_setopt(curl, CURLOPT_PROXY, std::string(settings.m_strProxyHostname.ToUTF8()).c_str());
228
curl_easy_setopt(curl, CURLOPT_PROXYPORT, settings.m_nProxyPort);
229
if (settings.m_bRequiresAuth) {
230
curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME,std::string(settings.m_strProxyUsername.ToUTF8()).c_str());
231
curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, std::string(settings.m_strProxyPassword.ToUTF8()).c_str());
233
curl_easy_setopt(curl, CURLOPT_NOPROXY, std::string(settings.m_strProxyExceptions.ToUTF8()).c_str());
236
curl_easy_setopt(curl, CURLOPT_HTTPGET, TRUE);
238
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fileResponseHandler);
239
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &responseDataHandler);
241
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA , this);
242
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progress_func_download_cloud);
243
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0);
245
res = curl_easy_perform(curl);
248
if(res != CURLE_OK) {
249
std::ostringstream os;
250
os << _Std("Could to retrieve study.") << " " << curl_easy_strerror(res);
251
m_pCloudParams->m_error = os.str();
252
LOG_ERROR("CloudGet", m_pCloudParams->m_error);
256
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
257
if (http_code != 200) {
258
std::ostringstream os;
259
if (http_code == 410) {
260
//version is not supported
261
os << _Std("This version of Ginkgo CADx is not compatible with current version of Ginkgo Cloud.");
263
os << _Std("Error retrieving study.") << " Http error code: " << http_code;
265
m_pCloudParams->m_error = os.str();
266
LOG_ERROR("CloudGet", m_pCloudParams->m_error);
268
// now extract transfer info
269
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
270
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
271
std::ostringstream ostr;
272
ostr << _Std("Study successfully retrieved.") << " " << _Std("Speed") << " " << responseDataHandler.filelen << " " << _Std("bytes/sec during") << " " << total_time << " " << _Std("seconds");
273
LOG_INFO("CloudGet", ostr.str());
274
if (responseDataHandler.filelen == 0) {
275
std::ostringstream os;
276
os << _Std("Empty Response");
277
m_pCloudParams->m_error = os.str();
278
LOG_ERROR("CloudGet", m_pCloudParams->m_error);
283
curl_easy_cleanup(curl);
285
responseDataHandler.fos->Close();
286
delete responseDataHandler.fos;
289
wxFileName fileIn ( tmpFile );
290
NotificarProgreso(0.95f, _Std("Extracting files..."));
292
wxFFileInputStream fsIn(fileIn.GetFullPath());
293
wxZlibInputStream gzIn(fsIn, wxZLIB_GZIP);
295
wxTarInputStream tarIn(gzIn);
297
std::auto_ptr<wxTarEntry> entry;
299
pParams->m_baseDir = GNC::GCS::IEntorno::Instance()->CrearDirectorioTemporal();
300
wxString baseTargetDir(FROMPATH(pParams->m_baseDir));
302
for (wxTarEntry* entry = tarIn.GetNextEntry(); entry != NULL; entry = tarIn.GetNextEntry()) {
303
if (entry->IsDir()) {
304
wxMkdir(baseTargetDir + wxFileName::GetPathSeparator() + entry->GetName());
306
wxFFileOutputStream fileOut(baseTargetDir + wxFileName::GetPathSeparator() + entry->GetName());
314
pParams->m_error = _Std("Invalid file has been downloaded");
317
pParams->m_error = _Std("Invalid file has been downloaded");
321
wxRemoveFile(fileIn.GetFullPath());
322
wxRmdir(fileIn.GetPath());
326
void GNC::GCS::CloudCommand::doFind(GNC::GCS::CloudFindCommandParams* pParams) {
328
// Step 1: Setup handler
329
InMemmoryDataHandler responseDataHandler;
330
memset(&responseDataHandler, 0, sizeof(InMemmoryDataHandler));
336
double speed_upload, total_time;
338
curl = curl_easy_init();
340
std::ostringstream baseUrl;
341
baseUrl << GINKGO_CLOUD_URI << "/find/";
342
baseUrl << pParams->m_bucketId;
344
curl_easy_setopt(curl, CURLOPT_URL, baseUrl.str().c_str());
346
std::ostringstream userAgent;
347
userAgent << "Mozilla/5.0 (compatible; GinkgoCADx " << GNC::GCS::IEntorno::Instance()->GetGinkgoVersionString() << " )";
348
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.str().c_str());
351
wxProxySettings settings;
352
settings.ProxySettingsLoadGeneral();
353
if (settings.m_bUseProxy) {
354
curl_easy_setopt(curl, CURLOPT_PROXY, std::string(settings.m_strProxyHostname.ToUTF8()).c_str());
355
curl_easy_setopt(curl, CURLOPT_PROXYPORT, settings.m_nProxyPort);
356
if (settings.m_bRequiresAuth) {
357
curl_easy_setopt(curl, CURLOPT_PROXYUSERNAME,std::string(settings.m_strProxyUsername.ToUTF8()).c_str());
358
curl_easy_setopt(curl, CURLOPT_PROXYPASSWORD, std::string(settings.m_strProxyPassword.ToUTF8()).c_str());
360
curl_easy_setopt(curl, CURLOPT_NOPROXY, std::string(settings.m_strProxyExceptions.ToUTF8()).c_str());
363
struct curl_httppost* post = NULL;
364
struct curl_httppost* last = NULL;
366
curl_formadd(&post, &last, CURLFORM_COPYNAME, "userName", CURLFORM_COPYCONTENTS, pParams->m_userNameQuery.c_str(), CURLFORM_END);
368
curl_formadd(&post, &last, CURLFORM_COPYNAME, "description", CURLFORM_COPYCONTENTS, pParams->m_descriptionQuery.c_str(), CURLFORM_END);
369
curl_formadd(&post, &last, CURLFORM_COPYNAME, "modalities", CURLFORM_COPYCONTENTS, pParams->m_modalitiesQuery.c_str(), CURLFORM_END);
370
curl_formadd(&post, &last, CURLFORM_COPYNAME, "fromDate", CURLFORM_COPYCONTENTS, pParams->m_fromDateQuery.c_str(), CURLFORM_END);
371
curl_formadd(&post, &last, CURLFORM_COPYNAME, "toDate", CURLFORM_COPYCONTENTS, pParams->m_toDateQuery.c_str(), CURLFORM_END);
372
curl_formadd(&post, &last, CURLFORM_COPYNAME, "versionId", CURLFORM_COPYCONTENTS, CLOUD_PROTOCOL_VERSION, CURLFORM_END);
374
curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
376
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, GNC::inMemmoryResponseHandler);
377
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &responseDataHandler);
379
res = curl_easy_perform(curl);
383
if(res != CURLE_OK) {
384
std::ostringstream os;
385
os << _Std("Could to perform query.") << " " << curl_easy_strerror(res);
386
m_pCloudParams->m_error = os.str();
387
LOG_ERROR("CloudFind", m_pCloudParams->m_error);
391
curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code);
392
if (http_code != 200) {
393
std::ostringstream os;
394
if (http_code == 410) {
395
//version is not supported
396
os << _Std("This version of Ginkgo CADx is not compatible with current version of Ginkgo Cloud.");
398
os << _Std("Error perfoming the query.") << " Http error code: " << http_code;
400
m_pCloudParams->m_error = os.str();
401
LOG_ERROR("CloudFind", m_pCloudParams->m_error);
403
// now extract transfer info
404
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD, &speed_upload);
405
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &total_time);
406
std::ostringstream ostr;
407
ostr << _Std("Query successfully performed.") << " " << _Std("Speed") << " " << speed_upload << " " << _Std("bytes/sec during") << " " << total_time << " " << _Std("seconds");
408
LOG_INFO("CloudFind", ostr.str());
413
if (responseDataHandler.bufferlen > 0 && m_pCloudParams->m_error.empty()) {
415
if (GNC::GCS::IControladorLog::Instance()->IsEnabledFor(GNC::GCS::IControladorLog::TraceLog)) {
416
std::string jsonData = std::string(responseDataHandler.data, responseDataHandler.bufferlen);
417
LOG_TRACE("CloudFind", _Std("JSon Response") << ": " << jsonData);
421
bool parsingSuccessful = reader.parse( responseDataHandler.data, responseDataHandler.data + responseDataHandler.bufferlen, root );
422
if (parsingSuccessful) {
424
for (Json::ValueIterator it1 = root.begin(); it1 != root.end(); ++it1) {
425
CloudFindCommandParams::ResultTuple rtuple;
426
Json::Value& tuple = (*it1);
428
Json::Value::Members m = tuple.getMemberNames();
429
for (Json::Value::Members::iterator it2 = m.begin(); it2 != m.end(); ++it2) {
430
const std::string& name = (*it2);
431
Json::Value& value = tuple[name];
432
if (value.isString()) {
433
rtuple[name] = tuple.get(name, "").asCString();
435
else if (value.isBool()) {
436
if (value.asBool()) {
437
rtuple[name] = "true";
440
rtuple[name] = "false";
443
else if (value.isInt()) {
444
std::ostringstream os;
446
rtuple[name] = os.str();
448
else if (value.isDouble()) {
449
std::ostringstream os;
451
rtuple[name] = os.str();
453
else if (value.isArray()) {
454
if (name == "modalities") {
456
Json::Value& modalities = tuple[name];
457
std::ostringstream os;
458
for (Json::ValueIterator it3 = modalities.begin(); it3 != modalities.end(); ++it3) {
459
Json::Value& modality = (*it3);
460
if (modality.isString()) {
467
os << modality.asCString();
470
rtuple[name] = os.str();
474
LOG_TRACE("CloudFind", _Std("Unknown JSon attribute:") << ": " << name);
478
pParams->m_pResults.push_back(rtuple);
483
std::ostringstream os;
484
os << _Std("Error parsing JSon: ");
485
os << reader.getFormatedErrorMessages();
486
m_pCloudParams->m_error = os.str();
487
LOG_ERROR("CloudFind", m_pCloudParams->m_error);
493
LOG_ERROR("CloudFind", _Std("Null Response"));
495
if (responseDataHandler.data != NULL) {
496
free(responseDataHandler.data);
499
curl_easy_cleanup(curl);
504
void GNC::GCS::CloudCommand::Update()
506
if (m_pCloudParams->m_error != "") {
507
GNC::GCS::IEventsController::Instance()->ProcesarEvento(new GNC::GCS::Events::EventoMensajes(NULL, m_pCloudParams->m_error, GNC::GCS::Events::EventoMensajes::PopUpMessage, false, GNC::GCS::Events::EventoMensajes::Error));
509
if (m_pCloudParams->m_action == CloudCommandParams::CC_GET) {
511
CloudGetCommandParams* pParams = static_cast<CloudGetCommandParams*>(m_pCloudParams);
513
if (pParams != NULL) {
514
GADAPI::ComandoIncluirHistorial::ComandoIncluirHistorialParams* pIncluirParams = new GADAPI::ComandoIncluirHistorial::ComandoIncluirHistorialParams(pParams->m_baseDir, true, GNC::GCS::IHistoryController::TAA_MOVE);
515
pIncluirParams->m_abrirDespuesDeCargar = true;
516
GADAPI::ComandoIncluirHistorial::ComandoIncluirHistorial* pCmd = new GADAPI::ComandoIncluirHistorial::ComandoIncluirHistorial(pIncluirParams);
517
GNC::GCS::ICommandController::Instance()->ProcessAsync(_Std("Storing in the history..."), pCmd, this->GetOwner());
b'\\ No newline at end of file'