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