1 //
2 // FLExportManager.cpp
3 //
4 //
5 // Created by Sarah Denoux on 13/05/13.
6 // Copyright (c) 2013 GRAME. All rights reserved.
7 //
8
9
10 #if defined(WIN32) && !defined(GCC)
11 # pragma warning (disable: 4100)
12 #else
13 # include <unistd.h>
14 # pragma GCC diagnostic ignored "-Wunused-variable"
15 # pragma GCC diagnostic ignored "-Wunused-function"
16 #endif
17
18 #ifdef WIN32
19 # define WIN32_LEAN_AND_MEAN // this is intended to solve the winsock API redefinitions
20 #endif
21
22 #include <fstream>
23 #include <map>
24 #include <vector>
25 #include <string>
26 #include <ctype.h>
27 #include <QtNetwork>
28 #include <QtGlobal>
29 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
30 # define Qt6 true
31 #else
32 # define Qt6 false
33 #endif
34
35 #include "utilities.h"
36
37 #define LLVM_DSP_FACTORY
38 #ifdef LLVM_DSP_FACTORY
39 #include "faust/dsp/llvm-dsp.h"
40 #else
41 #include "faust/dsp/interpreter-dsp.h"
42 #endif
43
44 #include "FLExportManager.h"
45 #include "FLSettings.h"
46 #include "QTDefs.h"
47
48
49 #ifndef QRCODECTRL
50 # define QRCODECTRL
51 #endif
52 #include "faust/gui/QTUI.h"
53 #include "SimpleParser.h"
54
55 #define JSON_ONLY
56
57
58 using namespace std;
59
60 FLTargetChooser* FLTargetChooser::_targetChooser = NULL;
61 FLExportManager* FLExportManager::_exportManager = NULL;
62
63 /*****************************FLTarget Chooser**********************************/
FLTargetChooser(QWidget * parent)64 FLTargetChooser::FLTargetChooser(QWidget* parent) : QDialog(parent){
65
66 fTargetReply = NULL;
67
68 fLastPlatform = "";
69 fLastArchi = "";
70 fLastChoice = "";
71
72 init();
73 }
74
~FLTargetChooser()75 FLTargetChooser::~FLTargetChooser(){
76
77 abortReply();
78
79 setLastState();
80 }
81
_Instance()82 FLTargetChooser* FLTargetChooser::_Instance(){
83
84 if(FLTargetChooser::_targetChooser == NULL)
85 FLTargetChooser::_targetChooser = new FLTargetChooser;
86
87 _targetChooser->sendTargetRequest();
88
89 return FLTargetChooser::_targetChooser;
90 }
91
92 //User Demande for the platforms
sendTargetRequest()93 void FLTargetChooser::sendTargetRequest(){
94
95 // prepare plaform menu
96 fPlatforms.clear();
97 fTargets.clear();
98 fExportPlatform->clear();
99 fExportArchi->clear();
100 fErrorText->setText("Searching for available targets...");
101
102 QString targetUrl= FLSettings::_Instance()->value("General/Network/FaustWebUrl", "http://faustservice.grame.fr").toString();
103 targetUrl += "/targets";
104 // Reset available targets
105
106 QNetworkRequest request(targetUrl);
107 QNetworkAccessManager * manager = new QNetworkAccessManager;
108
109 fTargetReply = manager->get(request);
110 connect(fTargetReply, SIGNAL(finished()), this, SLOT(targetsDescriptionReceived()));
111 }
112
113 //Init graphical elements of Export Manager Menu
init()114 void FLTargetChooser::init(){
115 QFormLayout* exportLayout = new QFormLayout;
116
117 QString title("<h2>Export Manager</2>");
118
119 QLabel* dialogTitle = new QLabel(title);
120 dialogTitle->setAlignment(Qt::AlignCenter);
121
122 exportLayout->addRow(dialogTitle);
123
124 fMenu2Export = new QGroupBox(this);
125 fMenu2Layout = new QGridLayout;
126
127 fExportPlatform = new QComboBox(fMenu2Export);
128 #if Qt6
129 connect(fExportPlatform, SIGNAL(currentIndexChanged(int)), this, SLOT(platformChanged(int)));
130 #else
131 connect(fExportPlatform, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(platformChanged(const QString&)));
132 #endif
133
134 fExportArchi = new QComboBox(fMenu2Export);
135
136 fExportChoice = new QComboBox(fMenu2Export);
137 fExportChoice->addItem("binary.zip");
138 fExportChoice->addItem("binary.apk");
139 // fExportChoice->addItem("binary.html");
140 fExportChoice->addItem("src.cpp");
141
142 fMenu2Layout->addWidget(new QLabel("Platform"), 0, 0);
143 fMenu2Layout->addWidget(fExportPlatform, 0, 1);
144 fMenu2Layout->addWidget(new QLabel("Architecture"), 1, 0);
145 fMenu2Layout->addWidget(fExportArchi, 1, 1);
146 fMenu2Layout->addWidget(new QLabel("source or binary"), 2, 0);
147 fMenu2Layout->addWidget(fExportChoice, 2, 1);
148
149 fErrorText = new QLabel("");
150 fMenu2Layout->addWidget(fErrorText, 3, 0, 1, 3);
151
152 fMenu2Export->setLayout(fMenu2Layout);
153 exportLayout->addRow(fMenu2Export);
154
155 QWidget* intermediateWidget = new QWidget(this);
156 QHBoxLayout* intermediateLayout = new QHBoxLayout;
157
158 QPushButton* cancel = new QPushButton(tr("Cancel"), intermediateWidget);
159 cancel->setDefault(false);
160 cancel->show();
161
162 QPushButton* saveButton = new QPushButton(tr("Export"), intermediateWidget);
163 saveButton->setDefault(true);
164 saveButton->show();
165
166 connect(saveButton, SIGNAL(released()), this, SLOT(acceptDialog()));
167 connect(cancel, SIGNAL(released()), this, SLOT(cancelDialog()));
168 connect(cancel, SIGNAL(released()), this, SLOT(abortReply()));
169
170 intermediateLayout->addWidget(cancel);
171 intermediateLayout->addWidget(new QLabel(tr("")));
172 intermediateLayout->addWidget(saveButton);
173
174 intermediateWidget->setLayout(intermediateLayout);
175 exportLayout->addRow(intermediateWidget);
176
177 setLayout(exportLayout);
178 centerOnPrimaryScreen(this);
179 }
180
181 //Keeping in memory the graphical state of the dialog
setLastState()182 void FLTargetChooser::setLastState(){
183
184 fLastPlatform = fExportPlatform->currentText();
185 fLastArchi = fExportArchi->currentText();
186 fLastChoice = fExportArchi->currentText();
187 }
188
closeEvent(QCloseEvent *)189 void FLTargetChooser::closeEvent(QCloseEvent* /*event*/){
190 cancelDialog();
191 }
192
cancelDialog()193 void FLTargetChooser::cancelDialog(){
194 setLastState();
195 reject();
196 }
197
acceptDialog()198 void FLTargetChooser::acceptDialog(){
199 setLastState();
200 accept();
201 }
202
203 //Build Graphical lists of OS and Platforms received from the server
targetsDescriptionReceived()204 void FLTargetChooser::targetsDescriptionReceived(){
205
206 fErrorText->setText("");
207
208 QNetworkReply* response = (QNetworkReply*)QObject::sender();
209
210 if(response->error() == QNetworkReply::NoError){
211
212 // prepare plaform menu
213 fPlatforms.clear();
214 fTargets.clear();
215 fExportPlatform->clear();
216 fExportArchi->clear();
217
218 QByteArray key = response->readAll();
219 const char* p = key.data();
220
221 if (parseOperatingSystemsList(p, fPlatforms, fTargets)) {
222
223 for (size_t i=0; i<fPlatforms.size();i++)
224 fExportPlatform->addItem(fPlatforms[i].c_str());
225
226 fExportPlatform->show();
227
228 // prepare architecture menu
229 vector<string> archs = fTargets[fPlatforms[0]];
230
231 for (size_t i=0; i<archs.size();i++)
232 fExportArchi->addItem(archs[i].c_str());
233
234 fExportArchi->show();
235
236 // Recall last target choices
237 int index = fExportPlatform->findText(fLastPlatform);
238 if(index != -1)
239 fExportPlatform->setCurrentIndex(index);
240
241 index = fExportArchi->findText(fLastArchi);
242 if(index != -1)
243 fExportArchi->setCurrentIndex(index);
244
245 index = fExportChoice->findText(fLastChoice);
246 if(index != -1)
247 fExportChoice->setCurrentIndex(index);
248 }
249 else {
250
251 fErrorText->setText("Targets Could not be parsed.");
252 }
253 }
254 else{
255 fErrorText->setText("Web Service is not available.\nVerify the web service URL in the preferences.");
256 }
257
258 fTargetReply = NULL;
259 }
260
261 //Dynamic changes of the available architectures depending on platform
platformChanged(const QString & index)262 void FLTargetChooser::platformChanged(const QString& index){
263
264 fExportArchi->hide();
265 fExportArchi->clear();
266
267 vector<string> architectures = fTargets[index.toStdString()];
268 vector<string>::iterator it;
269
270 for (it = architectures.begin(); it!=architectures.end(); it++) {
271 fExportArchi->addItem((*it).c_str());
272 }
273 fExportArchi->show();
274 }
275
276 //Dynamic changes of the available architectures depending on platform
platformChanged(int index)277 void FLTargetChooser::platformChanged(int index){
278 platformChanged(fExportPlatform->itemText(index));
279 }
280
281 //When Cancel is pressed, the request is aborted
abortReply()282 void FLTargetChooser::abortReply(){
283
284 if(fTargetReply) {
285 disconnect(fTargetReply, 0, 0, 0);
286
287 if(fTargetReply->isRunning())
288 fTargetReply->close();
289
290 fTargetReply->deleteLater();
291 }
292 }
293
294 //---- Accessors to current choices
platform()295 QString FLTargetChooser::platform(){
296 return fExportPlatform->currentText();
297 }
298
architecture()299 QString FLTargetChooser::architecture(){
300 return fExportArchi->currentText();
301 }
302
binOrSource()303 QString FLTargetChooser::binOrSource(){
304 return fExportChoice->currentText();
305 }
306
307 /*****************************FLExport Manager**********************************/
308
FLExportManager()309 FLExportManager::FLExportManager(){
310
311 fStep = 0;
312
313 fPostReply = NULL;
314 fGetKeyReply = NULL;
315
316 fTextZone = NULL;
317 fQrCodeLabel = NULL;
318
319 fLastOpened = getenv("HOME");
320
321 init();
322
323 QDir ImagesDir(":/");
324 ImagesDir.cd("Images");
325
326 fCheckImg = QPixmap(ImagesDir.absoluteFilePath("Check.png"));
327 fNotCheckImg = QPixmap(ImagesDir.absoluteFilePath("NotCheck.png"));
328
329 }
330
~FLExportManager()331 FLExportManager::~FLExportManager(){
332
333 // In case the export is aborted during its execution
334 if(fPostReply)
335 abortReply(fPostReply);
336 if(fGetKeyReply)
337 abortReply(fGetKeyReply);
338 }
339
_Instance()340 FLExportManager* FLExportManager::_Instance(){
341 if(_exportManager == NULL)
342 _exportManager = new FLExportManager;
343
344 return _exportManager;
345 }
346
347 //Displaying the progress of remote compilation
init()348 void FLExportManager::init(){
349
350 QLabel* title = new QLabel(tr("<h2>Export Manager</h2>"));
351 title->setAlignment(Qt::AlignCenter);
352
353 fMsgLayout = new QGridLayout;
354
355 fCheck1 = new QLabel("");
356 fConnectionLabel = new QLabel(tr("Connection to Server"));
357
358 fCheck2 = new QLabel("");
359 fCompilationLabel = new QLabel(tr("Remote Compilation"));
360
361 QPushButton* closeB = new QPushButton(tr("Cancel"), this);
362 fSaveB = new QPushButton(tr("Save"), this);
363 fOkB = new QPushButton(tr("Ok"), this);
364
365 fPrgBar = new QProgressDialog;
366 fPrgBar->setRange(0,0);
367 fPrgBar->setCancelButton(NULL);
368
369 fTextZone = new QTextEdit;
370 fTextZone->setReadOnly(true);
371
372 fQrCodeLabel = new QLabel;
373
374 fMsgLayout->addWidget(title, 0, 0, 1, 0, Qt::AlignCenter);
375 fMsgLayout->addWidget(new QLabel(tr("")), 1, 0, 1, 1, Qt::AlignCenter);
376 fMsgLayout->addWidget(fConnectionLabel, 2, 0, Qt::AlignCenter);
377 fMsgLayout->addWidget(fCheck1, 2, 1, Qt::AlignCenter);
378 fMsgLayout->addWidget(fCompilationLabel, 3, 0, Qt::AlignCenter);
379 fMsgLayout->addWidget(fCheck2, 3, 1, Qt::AlignCenter);
380 fMsgLayout->addWidget(new QLabel(tr("")), 4, 0, 1, 4, Qt::AlignCenter);
381 fMsgLayout->addWidget(fPrgBar, 5, 0, 1, 5, Qt::AlignCenter);
382 fMsgLayout->addWidget(fTextZone, 5, 0, 1, 5, Qt::AlignCenter);
383 fMsgLayout->addWidget(new QLabel(tr("")), 6, 0, 1, 6, Qt::AlignCenter);
384 fMsgLayout->addWidget(closeB, 7, 0, Qt::AlignCenter);
385 fMsgLayout->addWidget(fSaveB, 7, 1, Qt::AlignCenter);
386 fMsgLayout->addWidget(fOkB, 7, 0, 1, 7, Qt::AlignCenter);
387
388 connect(closeB, SIGNAL(released()), this, SLOT(hide()));
389 connect(closeB, SIGNAL(released()), this, SLOT(redirectAbort()));
390 connect(fSaveB, SIGNAL(released()), this, SLOT(saveFileOnDisk()));
391 connect(fOkB, SIGNAL(released()), this, SLOT(hide()));
392
393 setLayout(fMsgLayout);
394 adjustSize();
395 hide();
396 centerOnPrimaryScreen(this);
397 }
398
closeEvent(QCloseEvent *)399 void FLExportManager::closeEvent(QCloseEvent* /*event*/){
400 redirectAbort();
401 hide();
402 }
403
404 //When Cancel is pressed, the request is aborted
abortReply(QNetworkReply * reply)405 void FLExportManager::abortReply(QNetworkReply* reply){
406
407 if(reply) {
408 disconnect(reply, 0, 0, 0);
409
410 if(reply->isRunning())
411 reply->close();
412
413 reply->deleteLater();
414 }
415 }
416
redirectAbort()417 void FLExportManager::redirectAbort(){
418
419 QNetworkReply* response;
420
421 if(fStep == 1)
422 response = fPostReply;
423 else if(fStep == 2)
424 response = fGetKeyReply;
425 else
426 return;
427
428 disconnect(response, 0, 0, 0);
429 }
430
431 //Access Point for FaustLive to export a file
exportFile(const QString & name,const QString & faustCode,const QString & p,const QString & a,const QString & sb)432 void FLExportManager::exportFile(const QString& name, const QString& faustCode, const QString& p, const QString& a, const QString& sb){
433
434 fPlatform = p;
435 fArchi = a;
436 fChoice = sb;
437
438 fAppName = name;
439 fCodeToSend = faustCode;
440
441 // Reset message dialog graphical elements
442 fStep = 0;
443 fTextZone->clear();
444 fSaveB->hide();
445 fOkB->hide();
446 fCheck1->hide();
447 fConnectionLabel->hide();
448 fCheck2->hide();
449 fCompilationLabel->hide();
450 fTextZone->hide();
451 fQrCodeLabel->hide();
452
453 setVisible(true);
454
455 postExport();
456 }
457
458 //Upload the file to the server with a post request
postExport()459 void FLExportManager::postExport(){
460
461 fStep++;
462
463 fConnectionLabel->show();
464 fPrgBar->show();
465 adjustSize();
466 show();
467
468 QString destinedUrl = FLSettings::_Instance()->value("General/Network/FaustWebUrl", "http://faustservice.grame.fr").toString();
469
470 QNetworkRequest requete(destinedUrl);
471 QNetworkAccessManager *m = new QNetworkAccessManager;
472
473 /* This way of encoding might seem random but it's the only one working !! */
474 //The boundary to recognize the end of the file. It should be random.
475 QByteArray boundary = "87142694621188";
476 QByteArray data;
477
478 // Open the file to send
479 data = "--" + boundary + "\r\n";
480 data += "Content-Disposition: form-data; name=\"file\"; filename=\"";
481 data += fAppName.toUtf8();
482 data += ".dsp\";\r\nContent-Type: text/plain\r\n\r\n";
483 data += fCodeToSend.toUtf8();
484 data += "\r\n--" + boundary + "--\r\n";
485
486 requete.setRawHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
487 requete.setRawHeader("Content-Length", QString::number(data.size()).toLatin1());
488
489 fPostReply = m->post(requete, data);
490
491 connect(fPostReply, SIGNAL(finished()), this, SLOT(readKey()));
492 connect(fPostReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError)));
493 }
494
495 //Receiving the sha key generated by the server, responding to the post request
readKey()496 void FLExportManager::readKey(){
497
498 QNetworkReply* response = (QNetworkReply*)QObject::sender();
499
500 if(response->error() == QNetworkReply::NoError){
501
502 fStep++;
503
504 fCheck1->setPixmap(fCheckImg);
505 fCheck1->show();
506
507 fCompilationLabel->show();
508
509 QByteArray key = response->readAll();
510
511 getFileFromKey(key.data());
512 }
513
514 fPostReply = NULL;
515 }
516
517 //When the Server sends back an error
networkError(QNetworkReply::NetworkError)518 void FLExportManager::networkError(QNetworkReply::NetworkError /*msg*/){
519
520 QNetworkReply* response = (QNetworkReply*)QObject::sender();
521
522 if(fStep == 1){
523 fCheck1->setPixmap(fNotCheckImg);
524 fCheck1->show();
525 }
526 else{
527 fCheck2->setPixmap(fNotCheckImg);
528 fCheck2->show();
529 }
530 fPrgBar->hide();
531
532 fTextZone->setText(response->errorString());
533 fTextZone->show();
534 }
535
536 //Send new request : urlServer/SHA1Key/Platform/Architecture/BinaryOrSource
getFileFromKey(const char * key)537 void FLExportManager::getFileFromKey(const char* key){
538
539 fStep++;
540
541 fUrl = FLSettings::_Instance()->value("General/Network/FaustWebUrl", "http://faustservice.grame.fr").toString();
542 fUrl += ("/");
543 fUrl += key;
544 fUrl += "/";
545
546 fUrl += fPlatform;
547 fUrl += "/";
548 fUrl += fArchi;
549 fUrl += "/";
550 fUrl += fChoice;
551
552 const QUrl url = QUrl(fUrl);
553 QNetworkRequest requete(url);
554 QNetworkAccessManager *m = new QNetworkAccessManager;
555
556 QNetworkReply * fGetKeyReply = m->get(requete);
557
558 connect(fGetKeyReply, SIGNAL(finished()), this, SLOT(showSaveB()));
559 connect(fGetKeyReply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError)));
560 }
561
562 //Manage the file received from server
saveFileOnDisk()563 void FLExportManager::saveFileOnDisk(){
564
565 QFileDialog* fileDialog = new QFileDialog;
566 #ifdef QTNEWCONFIRMOVERWRITE
567 fileDialog->setOption(QFileDialog::DontConfirmOverwrite, false);
568 #else
569 fileDialog->setConfirmOverwrite(true);
570 #endif
571 QString filenameToSave;
572
573 // nom par défaut dans le dialogue
574
575 QString fullName = fAppName + "_" + fPlatform + "_" + fArchi;
576
577 QString defaultFilename = fLastOpened + "/" + fullName;
578
579 if(fChoice.compare("src.cpp") == 0){
580 defaultFilename += ".cpp";
581
582 filenameToSave = fileDialog->getSaveFileName(NULL, "Save File", defaultFilename, tr("(*.cpp)"));
583 }
584 else{
585
586 //s defaultFilename += ".zip";
587
588 filenameToSave = fileDialog->getSaveFileName(NULL, "Save File", defaultFilename, tr("(*.zip)"));
589 }
590
591 if(filenameToSave != ""){
592
593 fLastOpened = QFileInfo(filenameToSave).absolutePath();
594
595 QFile f(filenameToSave); //On ouvre le fichier
596
597 if ( f.open(QIODevice::WriteOnly) )
598 {
599 f.write(fDataReceived); ////On lit la réponse du serveur que l'on met dans un fichier
600 f.close(); //On ferme le fichier
601 }
602 }
603 hide();
604 }
605
showSaveB()606 void FLExportManager::showSaveB(){
607
608 QNetworkReply* response = (QNetworkReply*)QObject::sender();
609
610 if(response->error() == QNetworkReply::NoError){
611
612 fDataReceived = response->readAll();
613 fCheck2->setPixmap(fCheckImg);
614 fCheck2->show();
615 fPrgBar->hide();
616
617 QString sucessMsg = fAppName + "_" + fPlatform + "_" + fArchi;
618 sucessMsg += " was successfully exported";
619
620 fTextZone->setText(sucessMsg);
621 fTextZone->show();
622
623 if(fChoice == "ios" || fChoice == "binary.apk" || fChoice == "binary.html"){
624
625 //Construction of the flashcode
626 const int padding = 5;
627
628 printf("URL TO BUILD QRCODE = %s\n", fUrl.toStdString().c_str());
629 QImage image = getQRCode (fUrl, padding);
630 QImage big = image.scaledToWidth(image.width() * 4);
631 QPixmap fQrCode = QPixmap::fromImage(big);
632 fQrCodeLabel->setPixmap(fQrCode);
633 fMsgLayout->addWidget(fQrCodeLabel, 6, 0, 1, 6, Qt::AlignCenter);
634 fQrCodeLabel->show();
635 }
636 fSaveB->show();
637
638 response->deleteLater(); //IMPORTANT : on emploie la fonction deleteLater() pour supprimer la réponse du serveur.
639 //Si vous ne le faites pas, vous risquez des fuites de mémoire ou autre.
640 }
641
642 }
643
644
645
646
647
648