1 /*
2 * OpenSCAD (www.openscad.org)
3 * Copyright (C) 2009-2019 Clifford Wolf <clifford@clifford.at> and
4 * Marius Kintel <marius@kintel.net>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * As a special exception, you have permission to link this program
12 * with the CGAL library and distribute executables, as long as you
13 * follow the requirements of the GNU GPL in regard to all of the
14 * software in the executable aside from CGAL.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 *
25 */
26
27 #include "printutils.h"
28 #include "boost-utils.h"
29 #include "PrintService.h"
30
31 std::mutex PrintService::printServiceMutex;
32
PrintService()33 PrintService::PrintService()
34 {
35 enabled = false;
36 try {
37 init();
38 } catch (const NetworkException& e) {
39 LOG(message_group::Error,Location::NONE,"","%1$s",e.getErrorMessage());
40 }
41 if (enabled) {
42 LOG(message_group::None,Location::NONE,"","External print service available: %1$s (upload limit = %2$d MB)",displayName.toStdString(),fileSizeLimitMB);
43 }
44 }
45
~PrintService()46 PrintService::~PrintService()
47 {
48 }
49
inst()50 PrintService * PrintService::inst()
51 {
52 static PrintService *instance = nullptr;
53
54 std::lock_guard<std::mutex> lock(printServiceMutex);
55
56 if (instance == nullptr) {
57 instance = new PrintService();
58 }
59
60 return instance;
61 }
62
init()63 void PrintService::init()
64 {
65 auto networkRequest = NetworkRequest<void>{QUrl{"https://files.openscad.org/print-service.json"}, { 200 }, 30};
66 return networkRequest.execute(
67 [](QNetworkRequest& request) {
68 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
69 },
70 [](QNetworkAccessManager& nam, QNetworkRequest& request) {
71 return nam.get(request);
72 },
73 [&](QNetworkReply *reply) {
74 const auto doc = QJsonDocument::fromJson(reply->readAll());
75 PRINTDB("Response: %s", QString{doc.toJson()}.toStdString());
76 const QStringList services = doc.object().keys();
77 if (services.length() >= 1) {
78 service = services.at(0);
79 if (!service.isEmpty()) {
80 initService(doc.object().value(service).toObject());
81 }
82 }
83 }
84 );
85 }
86
initService(const QJsonObject & serviceObject)87 void PrintService::initService(const QJsonObject& serviceObject)
88 {
89 displayName = serviceObject.value("displayName").toString();
90 apiUrl = serviceObject.value("apiUrl").toString();
91 fileSizeLimitMB = serviceObject.value("fileSizeLimitMB").toInt();
92 infoHtml = serviceObject.value("infoHtml").toString();
93 infoUrl = serviceObject.value("infoUrl").toString();
94 enabled = !displayName.isEmpty() && !apiUrl.isEmpty() && !infoHtml.isEmpty() && !infoUrl.isEmpty() && fileSizeLimitMB != 0;
95 }
96
97 /**
98 * This function uploads a design to the 3D printing API endpoint and
99 * returns an URL that, when accessed, will show the design as a part
100 * that can be configured and added to the shopping cart. If it's not
101 * successful, it throws an exception with a message.
102 *
103 * Inputs:
104 * fileName - Then name we should give the file when it is uploaded
105 * for the order process.
106 * Outputs:
107 * The resulting url to go to next to continue the order process.
108 */
upload(const QString & fileName,const QString & contentBase64,network_progress_func_t progress_func)109 const QString PrintService::upload(const QString& fileName, const QString& contentBase64, network_progress_func_t progress_func)
110 {
111 QJsonObject jsonInput;
112 jsonInput.insert("fileName", fileName);
113 jsonInput.insert("file", contentBase64);
114
115 // Safe guard against QJson silently dropping the file content if it's
116 // too big. This seems to be configured at MaxSize = (1<<27) - 1 in Qt
117 // via qtbase/src/corelib/json/qjson_p.h
118 // Due to the base64 encoding having 33% overhead, that should allow for
119 // about 96MB data.
120 if (jsonInput.value("file") == QJsonValue::Undefined) {
121 const QString msg = "Could not encode STL into JSON. Perhaps it is too large of a file? Maybe try reducing the model resolution.";
122 throw NetworkException(QNetworkReply::ProtocolFailure, msg);
123 }
124
125 auto networkRequest = NetworkRequest<const QString>{QUrl{apiUrl}, { 200, 201 }, 180};
126 networkRequest.set_progress_func(progress_func);
127 return networkRequest.execute(
128 [](QNetworkRequest& request) {
129 request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
130 },
131 [&](QNetworkAccessManager& nam, QNetworkRequest& request) {
132 return nam.post(request, QJsonDocument(jsonInput).toJson());
133 },
134 [](QNetworkReply *reply) {
135 const auto doc = QJsonDocument::fromJson(reply->readAll());
136 PRINTDB("Response: %s", QString{doc.toJson()}.toStdString());
137
138 // Extract cartUrl which gives the page to open in a webbrowser to view uploaded part
139 const auto cartUrlValue = doc.object().value("data").toObject().value("cartUrl");
140 const auto cartUrl = cartUrlValue.toString();
141 if ((cartUrlValue == QJsonValue::Undefined) || (cartUrl.isEmpty())) {
142 const QString msg = "Could not get data.cartUrl field from response.";
143 throw NetworkException(QNetworkReply::ProtocolFailure, msg);
144 }
145 LOG(message_group::None,Location::NONE,"","Upload finished, opening URL %1$s.",cartUrl.toStdString());
146 return cartUrl;
147 }
148 );
149 }
150