aboutsummaryrefslogtreecommitdiff
path: root/backends/networking/curl
diff options
context:
space:
mode:
Diffstat (limited to 'backends/networking/curl')
-rw-r--r--backends/networking/curl/cloudicon.cpp171
-rw-r--r--backends/networking/curl/cloudicon.h70
-rw-r--r--backends/networking/curl/cloudicon_data.h111
-rw-r--r--backends/networking/curl/cloudicon_disabled_data.h117
-rw-r--r--backends/networking/curl/connectionmanager.cpp204
-rw-r--r--backends/networking/curl/connectionmanager.h132
-rw-r--r--backends/networking/curl/curljsonrequest.cpp215
-rw-r--r--backends/networking/curl/curljsonrequest.h67
-rw-r--r--backends/networking/curl/curlrequest.cpp162
-rw-r--r--backends/networking/curl/curlrequest.h100
-rw-r--r--backends/networking/curl/networkreadstream.cpp257
-rw-r--r--backends/networking/curl/networkreadstream.h142
-rw-r--r--backends/networking/curl/request.cpp74
-rw-r--r--backends/networking/curl/request.h203
14 files changed, 2025 insertions, 0 deletions
diff --git a/backends/networking/curl/cloudicon.cpp b/backends/networking/curl/cloudicon.cpp
new file mode 100644
index 0000000000..1c1ecf2f85
--- /dev/null
+++ b/backends/networking/curl/cloudicon.cpp
@@ -0,0 +1,171 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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 "backends/networking/curl/cloudicon.h"
+#include "backends/cloud/cloudmanager.h"
+#include "common/memstream.h"
+#include "gui/ThemeEngine.h"
+#include "gui/gui-manager.h"
+#include "image/png.h"
+
+namespace Networking {
+
+const float CloudIcon::ALPHA_STEP = 0.025;
+const float CloudIcon::ALPHA_MAX = 1;
+const float CloudIcon::ALPHA_MIN = 0.6;
+
+CloudIcon::CloudIcon():
+ _wasVisible(false), _iconsInited(false), _showingDisabled(false),
+ _currentAlpha(0), _alphaRising(true), _disabledFrames(0) {
+ initIcons();
+}
+
+CloudIcon::~CloudIcon() {}
+
+bool CloudIcon::draw() {
+ bool stop = false;
+ initIcons();
+
+ if (CloudMan.isWorking() || _disabledFrames > 0) {
+ if (g_system) {
+ if (!_wasVisible) {
+ g_system->clearOSD();
+ _wasVisible = true;
+ }
+ --_disabledFrames;
+ if (_alphaRising) {
+ if (_currentAlpha < ALPHA_MIN)
+ _currentAlpha += 5 * ALPHA_STEP;
+ else
+ _currentAlpha += ALPHA_STEP;
+ if (_currentAlpha > ALPHA_MAX) {
+ _currentAlpha = ALPHA_MAX;
+ _alphaRising = false;
+ }
+ } else {
+ _currentAlpha -= ALPHA_STEP;
+ if (_currentAlpha < ALPHA_MIN) {
+ _currentAlpha = ALPHA_MIN;
+ _alphaRising = true;
+ }
+ }
+ } else {
+ _wasVisible = false;
+ }
+ } else {
+ _wasVisible = false;
+ _currentAlpha -= 5 * ALPHA_STEP;
+ if (_currentAlpha <= 0) {
+ _currentAlpha = 0;
+ stop = true;
+ }
+ }
+
+ if (g_system) {
+ Graphics::TransparentSurface *surface = &_icon;
+ makeAlphaIcon((_showingDisabled ? _disabledIcon : _icon), _currentAlpha);
+ if (_alphaIcon.getPixels())
+ surface = &_alphaIcon;
+ if (surface && surface->getPixels()) {
+ int x = g_system->getOverlayWidth() - surface->w - 10, y = 10;
+ g_system->copyRectToOSD(surface->getPixels(), surface->pitch, x, y, surface->w, surface->h);
+ }
+ }
+
+ if (stop)
+ _showingDisabled = false;
+ return stop;
+}
+
+void CloudIcon::showDisabled() {
+ _showingDisabled = true;
+ _disabledFrames = 20 * 3; //3 seconds 20 fps
+}
+
+#include "backends/networking/curl/cloudicon_data.h"
+#include "backends/networking/curl/cloudicon_disabled_data.h"
+
+void CloudIcon::initIcons() {
+ if (_iconsInited)
+ return;
+ loadIcon(_icon, cloudicon_data, ARRAYSIZE(cloudicon_data));
+ loadIcon(_disabledIcon, cloudicon_disabled_data, ARRAYSIZE(cloudicon_disabled_data));
+ _iconsInited = true;
+}
+
+void CloudIcon::loadIcon(Graphics::TransparentSurface &icon, byte *data, uint32 size) {
+ Image::PNGDecoder decoder;
+ Common::MemoryReadStream stream(data, size);
+ if (!decoder.loadStream(stream))
+ error("CloudIcon::loadIcon: error decoding PNG");
+
+ Graphics::TransparentSurface *s = new Graphics::TransparentSurface(*decoder.getSurface(), true);
+ if (s) {
+ Graphics::PixelFormat f = g_system->getOSDFormat();
+ if (f != s->format) {
+ Graphics::TransparentSurface *s2 = s->convertTo(f);
+ if (s2)
+ icon.copyFrom(*s2);
+ else
+ warning("CloudIcon::loadIcon: failed converting TransparentSurface");
+ delete s2;
+ } else {
+ icon.copyFrom(*s);
+ }
+ delete s;
+ } else {
+ warning("CloudIcon::loadIcon: failed reading TransparentSurface from PNGDecoder");
+ }
+}
+
+void CloudIcon::makeAlphaIcon(Graphics::TransparentSurface &icon, float alpha) {
+ _alphaIcon.copyFrom(icon);
+
+ byte *pixels = (byte *)_alphaIcon.getPixels();
+ for (int y = 0; y < _alphaIcon.h; y++) {
+ byte *row = pixels + y * _alphaIcon.pitch;
+ for (int x = 0; x < _alphaIcon.w; x++) {
+ uint32 srcColor;
+ if (_alphaIcon.format.bytesPerPixel == 2)
+ srcColor = READ_UINT16(row);
+ else if (_alphaIcon.format.bytesPerPixel == 3)
+ srcColor = READ_UINT24(row);
+ else
+ srcColor = READ_UINT32(row);
+
+ // Update color's alpha
+ byte r, g, b, a;
+ _alphaIcon.format.colorToARGB(srcColor, a, r, g, b);
+ a = (byte)(a * alpha);
+ uint32 color = _alphaIcon.format.ARGBToColor(a, r, g, b);
+
+ if (_alphaIcon.format.bytesPerPixel == 2)
+ *((uint16 *)row) = color;
+ else
+ *((uint32 *)row) = color;
+
+ row += _alphaIcon.format.bytesPerPixel;
+ }
+ }
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/cloudicon.h b/backends/networking/curl/cloudicon.h
new file mode 100644
index 0000000000..7e4d7387a3
--- /dev/null
+++ b/backends/networking/curl/cloudicon.h
@@ -0,0 +1,70 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CLOUDICON_H
+#define BACKENDS_NETWORKING_CURL_CLOUDICON_H
+
+#include "graphics/transparent_surface.h"
+
+namespace Networking {
+
+class CloudIcon {
+ static const float ALPHA_STEP, ALPHA_MAX, ALPHA_MIN;
+
+ bool _wasVisible, _iconsInited, _showingDisabled;
+ Graphics::TransparentSurface _icon, _disabledIcon, _alphaIcon;
+ float _currentAlpha;
+ bool _alphaRising;
+ int _disabledFrames;
+
+ void initIcons();
+ void loadIcon(Graphics::TransparentSurface &icon, byte *data, uint32 size);
+ void makeAlphaIcon(Graphics::TransparentSurface &icon, float alpha);
+
+public:
+ CloudIcon();
+ ~CloudIcon();
+
+ /**
+ * This method is called from ConnectionManager every time
+ * its own timer calls the handle() method. The primary
+ * responsibility of this draw() method is to draw cloud icon
+ * on ScummVM's OSD when current cloud Storage is working.
+ *
+ * As we don't want ConnectionManager to work when no
+ * Requests are running, we'd like to stop the timer. But then
+ * this icon wouldn't have time to disappear smoothly. So,
+ * in order to do that, ConnectionManager stop its timer
+ * only when this draw() method returns true, indicating that
+ * the CloudIcon has disappeared and the timer could be stopped.
+ *
+ * @return true if ConnMan's timer could be stopped.
+ */
+ bool draw();
+
+ /** Draw a "cloud disabled" icon instead of "cloud syncing" one. */
+ void showDisabled();
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/curl/cloudicon_data.h b/backends/networking/curl/cloudicon_data.h
new file mode 100644
index 0000000000..21d88182a3
--- /dev/null
+++ b/backends/networking/curl/cloudicon_data.h
@@ -0,0 +1,111 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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.
+ *
+ */
+
+// This is a PNG file dumped into array.
+// $ recode data..d1 <dists/cloudicon.png >cloudicon_data.h
+// The tool is from https://github.com/pinard/Recode
+
+byte cloudicon_data[] = {
+ 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68,
+ 82, 0, 0, 0, 32, 0, 0, 0, 32, 8, 6, 0, 0, 0, 115,
+ 122, 122, 244, 0, 0, 0, 4, 115, 66, 73, 84, 8, 8, 8, 8,
+ 124, 8, 100, 136, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11,
+ 18, 0, 0, 11, 18, 1, 210, 221, 126, 252, 0, 0, 0, 22, 116,
+ 69, 88, 116, 67, 114, 101, 97, 116, 105, 111, 110, 32, 84, 105, 109,
+ 101, 0, 48, 54, 47, 48, 51, 47, 49, 54, 159, 192, 233, 192, 0,
+ 0, 0, 28, 116, 69, 88, 116, 83, 111, 102, 116, 119, 97, 114, 101,
+ 0, 65, 100, 111, 98, 101, 32, 70, 105, 114, 101, 119, 111, 114, 107,
+ 115, 32, 67, 83, 54, 232, 188, 178, 140, 0, 0, 4, 50, 73, 68,
+ 65, 84, 88, 133, 197, 151, 109, 104, 150, 101, 20, 199, 127, 247, 227,
+ 179, 105, 51, 23, 65, 181, 150, 224, 154, 214, 132, 194, 249, 33, 165,
+ 22, 189, 231, 194, 210, 250, 16, 171, 180, 55, 42, 152, 68, 65, 100,
+ 52, 233, 5, 146, 144, 144, 26, 249, 169, 62, 164, 80, 89, 152, 25,
+ 18, 226, 42, 49, 87, 88, 180, 94, 96, 96, 246, 234, 180, 70, 50,
+ 66, 214, 55, 247, 22, 133, 247, 255, 244, 225, 58, 247, 158, 107, 143,
+ 219, 243, 60, 186, 192, 3, 135, 251, 220, 215, 117, 223, 231, 127, 238,
+ 235, 252, 239, 235, 58, 39, 105, 250, 206, 56, 147, 146, 55, 85, 252,
+ 108, 13, 112, 59, 176, 12, 88, 2, 204, 7, 230, 248, 220, 48, 208,
+ 15, 244, 2, 221, 64, 23, 48, 86, 137, 211, 228, 146, 158, 178, 43,
+ 80, 7, 172, 3, 218, 129, 179, 43, 241, 9, 140, 0, 155, 129, 87,
+ 128, 193, 146, 15, 47, 248, 178, 100, 0, 237, 64, 39, 112, 14, 96,
+ 174, 20, 217, 153, 228, 162, 0, 50, 25, 2, 58, 128, 45, 83, 1,
+ 228, 53, 121, 10, 170, 129, 183, 128, 213, 126, 47, 215, 49, 224, 39,
+ 224, 19, 160, 7, 56, 2, 204, 0, 154, 128, 27, 128, 229, 110, 215,
+ 120, 64, 181, 132, 149, 184, 30, 120, 4, 248, 183, 24, 40, 185, 248,
+ 243, 147, 86, 160, 154, 144, 195, 91, 252, 43, 5, 140, 2, 31, 2,
+ 27, 129, 195, 83, 125, 141, 75, 19, 240, 2, 129, 47, 179, 61, 144,
+ 4, 216, 7, 172, 44, 14, 34, 105, 232, 62, 41, 128, 109, 192, 189,
+ 14, 126, 2, 24, 0, 30, 3, 246, 150, 1, 46, 150, 54, 15, 184,
+ 1, 200, 251, 216, 123, 192, 253, 19, 2, 152, 183, 119, 66, 0, 237,
+ 192, 27, 110, 159, 0, 250, 128, 187, 128, 67, 167, 8, 158, 201, 98,
+ 224, 3, 160, 209, 131, 72, 128, 53, 68, 156, 200, 153, 192, 181, 206,
+ 68, 167, 219, 50, 49, 96, 226, 78, 19, 135, 162, 103, 138, 117, 169,
+ 137, 46, 19, 163, 38, 82, 19, 63, 152, 120, 220, 196, 12, 159, 63,
+ 104, 98, 149, 137, 99, 238, 211, 28, 163, 46, 243, 145, 147, 192, 117,
+ 157, 68, 173, 219, 195, 18, 143, 74, 28, 137, 230, 139, 181, 77, 162,
+ 71, 98, 165, 68, 141, 68, 78, 98, 145, 196, 107, 18, 59, 252, 30,
+ 137, 3, 18, 207, 72, 140, 249, 125, 173, 99, 33, 65, 114, 209, 110,
+ 131, 192, 218, 65, 2, 105, 4, 108, 245, 165, 74, 125, 165, 206, 5,
+ 214, 2, 151, 3, 7, 128, 119, 128, 95, 253, 189, 169, 228, 105, 224,
+ 85, 183, 171, 128, 29, 192, 29, 4, 82, 142, 18, 246, 151, 177, 164,
+ 126, 151, 1, 220, 3, 188, 79, 32, 222, 40, 97, 167, 235, 243, 151,
+ 207, 2, 190, 5, 154, 35, 231, 223, 0, 45, 101, 242, 127, 12, 152,
+ 75, 97, 191, 88, 12, 124, 237, 254, 18, 96, 21, 176, 35, 227, 192,
+ 50, 207, 15, 38, 126, 49, 113, 56, 202, 243, 221, 38, 154, 139, 114,
+ 223, 82, 130, 23, 153, 214, 155, 88, 20, 221, 255, 104, 226, 104, 116,
+ 223, 106, 42, 144, 112, 169, 137, 196, 131, 248, 56, 10, 166, 222, 196,
+ 141, 21, 128, 77, 165, 223, 155, 120, 42, 34, 246, 158, 200, 247, 18,
+ 19, 228, 21, 178, 60, 223, 151, 41, 1, 190, 112, 251, 60, 224, 171,
+ 104, 238, 116, 36, 33, 240, 224, 32, 240, 25, 176, 31, 120, 194, 199,
+ 27, 161, 112, 26, 102, 167, 154, 1, 127, 186, 253, 98, 9, 240, 81,
+ 2, 97, 43, 149, 231, 61, 128, 129, 104, 108, 78, 28, 64, 44, 25,
+ 105, 218, 74, 56, 156, 13, 252, 76, 248, 43, 42, 145, 140, 176, 249,
+ 226, 137, 188, 133, 20, 12, 123, 68, 9, 225, 171, 127, 7, 46, 40,
+ 227, 180, 82, 112, 128, 153, 126, 189, 148, 194, 105, 57, 12, 5, 18,
+ 246, 71, 68, 185, 214, 237, 191, 166, 65, 190, 98, 237, 243, 235, 205,
+ 209, 88, 127, 188, 19, 246, 250, 53, 39, 177, 194, 237, 157, 37, 118,
+ 193, 83, 213, 183, 37, 102, 73, 92, 39, 145, 196, 152, 57, 75, 193,
+ 82, 246, 249, 21, 75, 89, 104, 41, 205, 150, 178, 222, 82, 250, 163,
+ 241, 211, 213, 253, 150, 178, 201, 82, 174, 180, 148, 185, 150, 146, 248,
+ 120, 183, 165, 133, 20, 116, 153, 24, 113, 123, 150, 137, 103, 77, 28,
+ 55, 113, 141, 137, 173, 38, 134, 60, 61, 167, 178, 236, 3, 38, 54,
+ 152, 184, 213, 255, 253, 39, 221, 55, 142, 213, 101, 42, 252, 5, 99,
+ 132, 202, 101, 45, 97, 175, 94, 14, 172, 0, 118, 1, 15, 185, 78,
+ 71, 86, 19, 138, 217, 196, 117, 179, 99, 146, 204, 126, 125, 188, 30,
+ 168, 35, 236, 255, 181, 132, 3, 233, 15, 224, 54, 202, 87, 64, 229,
+ 164, 5, 216, 9, 92, 232, 224, 67, 192, 66, 188, 88, 205, 69, 185,
+ 26, 180, 148, 14, 207, 81, 206, 82, 230, 89, 202, 110, 75, 185, 108,
+ 26, 249, 191, 202, 82, 222, 181, 148, 243, 163, 220, 119, 56, 22, 150,
+ 78, 172, 7, 144, 216, 34, 177, 205, 237, 188, 196, 2, 137, 61, 18,
+ 15, 158, 6, 243, 31, 144, 216, 37, 209, 224, 190, 18, 137, 237, 142,
+ 49, 254, 92, 50, 115, 211, 164, 69, 233, 71, 64, 43, 140, 23, 165,
+ 127, 19, 26, 142, 231, 8, 117, 192, 84, 82, 69, 56, 118, 215, 3,
+ 55, 17, 54, 160, 172, 40, 253, 148, 80, 168, 78, 44, 74, 171, 59,
+ 39, 237, 11, 170, 129, 55, 129, 251, 40, 108, 205, 2, 254, 1, 126,
+ 243, 96, 122, 129, 163, 62, 223, 0, 92, 237, 160, 141, 17, 112, 38,
+ 219, 129, 135, 139, 193, 1, 146, 170, 151, 43, 106, 76, 106, 139, 198,
+ 229, 192, 73, 20, 96, 12, 152, 177, 253, 56, 101, 26, 147, 36, 191,
+ 177, 226, 214, 108, 13, 133, 214, 172, 220, 75, 35, 14, 90, 190, 53,
+ 203, 189, 84, 113, 119, 156, 53, 167, 173, 192, 21, 252, 95, 205, 105,
+ 178, 225, 204, 182, 231, 255, 1, 200, 91, 112, 221, 160, 249, 68, 42,
+ 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130
+};
diff --git a/backends/networking/curl/cloudicon_disabled_data.h b/backends/networking/curl/cloudicon_disabled_data.h
new file mode 100644
index 0000000000..4340a8a37c
--- /dev/null
+++ b/backends/networking/curl/cloudicon_disabled_data.h
@@ -0,0 +1,117 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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.
+ *
+ */
+
+// This is a PNG file dumped into array.
+// $ recode data..d1 <dists/cloudicon_disabled.png >cloudicon_disabled_data.h
+// The tool is from https://github.com/pinard/Recode
+
+byte cloudicon_disabled_data[] = {
+ 137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68,
+ 82, 0, 0, 0, 32, 0, 0, 0, 32, 8, 6, 0, 0, 0, 115,
+ 122, 122, 244, 0, 0, 0, 4, 115, 66, 73, 84, 8, 8, 8, 8,
+ 124, 8, 100, 136, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11,
+ 18, 0, 0, 11, 18, 1, 210, 221, 126, 252, 0, 0, 0, 22, 116,
+ 69, 88, 116, 67, 114, 101, 97, 116, 105, 111, 110, 32, 84, 105, 109,
+ 101, 0, 48, 54, 47, 48, 51, 47, 49, 54, 159, 192, 233, 192, 0,
+ 0, 0, 28, 116, 69, 88, 116, 83, 111, 102, 116, 119, 97, 114, 101,
+ 0, 65, 100, 111, 98, 101, 32, 70, 105, 114, 101, 119, 111, 114, 107,
+ 115, 32, 67, 83, 54, 232, 188, 178, 140, 0, 0, 4, 139, 73, 68,
+ 65, 84, 88, 133, 197, 215, 91, 168, 86, 69, 20, 7, 240, 223, 254,
+ 244, 120, 236, 120, 57, 26, 94, 10, 31, 34, 35, 11, 36, 203, 74,
+ 212, 160, 204, 212, 110, 166, 248, 208, 205, 172, 151, 144, 136, 158, 82,
+ 80, 168, 32, 233, 165, 160, 32, 130, 236, 165, 34, 204, 74, 18, 52,
+ 36, 169, 232, 34, 24, 221, 243, 218, 133, 74, 77, 195, 44, 200, 91,
+ 122, 188, 156, 172, 102, 159, 221, 195, 204, 246, 219, 126, 231, 226, 17,
+ 138, 22, 12, 123, 246, 236, 153, 245, 95, 107, 205, 127, 205, 172, 157,
+ 21, 254, 95, 233, 27, 122, 63, 183, 5, 179, 48, 13, 87, 98, 52,
+ 6, 167, 111, 71, 176, 11, 27, 177, 14, 107, 209, 222, 27, 165, 217,
+ 137, 211, 207, 25, 137, 197, 152, 143, 65, 189, 209, 137, 163, 120, 1,
+ 79, 98, 111, 143, 147, 143, 247, 172, 108, 62, 158, 194, 16, 20, 169,
+ 105, 232, 151, 82, 171, 24, 80, 74, 27, 22, 225, 197, 110, 13, 104,
+ 235, 122, 188, 31, 94, 194, 93, 21, 192, 14, 49, 172, 223, 226, 109,
+ 124, 130, 29, 232, 131, 49, 184, 22, 55, 166, 126, 75, 131, 65, 175,
+ 225, 94, 252, 213, 201, 128, 131, 93, 131, 191, 137, 27, 42, 192, 199,
+ 241, 6, 158, 192, 246, 238, 188, 73, 50, 6, 143, 98, 78, 50, 164,
+ 140, 200, 123, 34, 135, 78, 49, 34, 219, 215, 89, 193, 171, 152, 151,
+ 192, 3, 246, 224, 1, 188, 123, 26, 224, 170, 92, 134, 143, 49, 160,
+ 50, 86, 96, 5, 238, 174, 78, 108, 204, 130, 249, 98, 216, 75, 240,
+ 109, 184, 13, 63, 156, 1, 248, 68, 44, 21, 189, 47, 165, 67, 140,
+ 196, 60, 172, 87, 225, 68, 182, 167, 62, 105, 100, 2, 106, 77, 11,
+ 118, 139, 123, 186, 163, 59, 164, 53, 76, 16, 195, 125, 29, 250, 15,
+ 227, 167, 73, 12, 237, 27, 73, 91, 75, 142, 28, 21, 185, 51, 60,
+ 141, 181, 225, 98, 41, 59, 106, 33, 185, 26, 88, 28, 104, 77, 253,
+ 163, 129, 251, 3, 59, 42, 223, 79, 105, 171, 184, 53, 240, 73, 224,
+ 150, 64, 203, 32, 106, 99, 185, 160, 224, 236, 16, 245, 118, 4, 54,
+ 5, 166, 4, 22, 6, 218, 211, 218, 214, 132, 37, 32, 75, 238, 181,
+ 224, 55, 12, 76, 222, 191, 140, 251, 144, 39, 79, 135, 98, 1, 198,
+ 98, 11, 150, 227, 251, 50, 204, 67, 113, 121, 90, 156, 57, 185, 127,
+ 135, 154, 184, 9, 95, 160, 9, 43, 49, 59, 69, 225, 24, 206, 65,
+ 123, 150, 54, 247, 14, 188, 158, 214, 30, 23, 79, 186, 109, 9, 252,
+ 44, 124, 142, 113, 149, 232, 127, 134, 201, 196, 88, 79, 66, 115, 5,
+ 252, 24, 182, 114, 224, 32, 35, 230, 212, 207, 139, 75, 241, 169, 168,
+ 47, 195, 157, 88, 89, 110, 193, 180, 64, 145, 250, 223, 5, 182, 87,
+ 194, 125, 123, 96, 92, 195, 22, 76, 14, 201, 227, 177, 226, 65, 144,
+ 227, 111, 28, 198, 151, 113, 131, 135, 5, 46, 169, 172, 249, 38, 176,
+ 187, 242, 62, 35, 160, 150, 199, 197, 19, 114, 178, 156, 34, 231, 173,
+ 244, 180, 154, 115, 115, 166, 166, 57, 167, 180, 193, 34, 3, 7, 165,
+ 61, 11, 137, 105, 27, 112, 160, 62, 111, 235, 106, 22, 166, 126, 71,
+ 206, 59, 165, 238, 156, 43, 114, 100, 155, 98, 120, 218, 146, 206, 2,
+ 83, 241, 225, 26, 134, 165, 253, 27, 173, 65, 134, 138, 137, 222, 154,
+ 222, 139, 228, 249, 87, 233, 217, 133, 76, 159, 19, 47, 169, 89, 226,
+ 129, 214, 71, 188, 192, 134, 148, 231, 64, 121, 171, 21, 248, 85, 244,
+ 232, 177, 30, 192, 59, 90, 82, 6, 193, 9, 108, 234, 30, 28, 30,
+ 9, 209, 128, 74, 214, 71, 204, 190, 121, 231, 201, 5, 228, 220, 218,
+ 248, 97, 136, 200, 246, 102, 106, 29, 234, 73, 190, 5, 135, 186, 7,
+ 135, 201, 9, 167, 111, 227, 135, 50, 2, 71, 146, 69, 153, 232, 245,
+ 206, 192, 136, 234, 196, 86, 49, 13, 154, 162, 113, 39, 211, 101, 51,
+ 126, 239, 25, 28, 154, 19, 206, 133, 234, 119, 195, 17, 234, 36, 220,
+ 85, 33, 202, 213, 169, 191, 175, 36, 92, 139, 152, 106, 3, 212, 9,
+ 119, 76, 100, 251, 126, 157, 9, 218, 69, 219, 150, 158, 211, 42, 99,
+ 187, 114, 245, 147, 112, 83, 122, 214, 2, 51, 83, 127, 85, 16, 89,
+ 62, 94, 60, 61, 202, 20, 58, 36, 38, 244, 126, 93, 159, 146, 93,
+ 180, 101, 129, 254, 129, 107, 2, 89, 26, 219, 24, 42, 6, 188, 95,
+ 153, 124, 81, 202, 251, 37, 3, 249, 101, 188, 120, 114, 148, 223, 219,
+ 197, 212, 56, 208, 123, 240, 245, 129, 167, 3, 19, 3, 163, 42, 6,
+ 172, 11, 234, 36, 92, 43, 242, 105, 32, 250, 227, 161, 89, 44, 45,
+ 98, 196, 139, 14, 178, 146, 112, 27, 210, 179, 23, 178, 7, 203, 240,
+ 248, 156, 152, 251, 15, 38, 221, 146, 138, 181, 212, 73, 216, 46, 214,
+ 112, 11, 82, 180, 103, 138, 71, 237, 40, 145, 52, 29, 216, 221, 194,
+ 220, 41, 49, 0, 103, 36, 129, 185, 152, 158, 116, 101, 9, 171, 29,
+ 178, 85, 245, 121, 213, 235, 184, 90, 215, 229, 248, 89, 172, 112, 190,
+ 62, 83, 112, 209, 145, 85, 226, 229, 147, 105, 188, 142, 43, 172, 220,
+ 155, 179, 40, 29, 201, 85, 6, 239, 204, 153, 155, 243, 117, 47, 216,
+ 222, 216, 38, 229, 188, 146, 51, 188, 162, 119, 81, 194, 82, 205, 130,
+ 178, 189, 24, 120, 173, 97, 108, 80, 34, 102, 111, 73, 87, 182, 123,
+ 2, 107, 2, 231, 133, 184, 213, 89, 96, 69, 194, 56, 57, 47, 91,
+ 222, 57, 100, 253, 68, 130, 92, 175, 94, 148, 254, 129, 15, 240, 176,
+ 88, 7, 116, 39, 77, 226, 181, 187, 68, 172, 146, 154, 69, 78, 101,
+ 98, 77, 57, 91, 99, 81, 250, 82, 215, 138, 202, 178, 188, 44, 78,
+ 37, 67, 254, 196, 143, 201, 152, 141, 98, 217, 86, 224, 60, 92, 149,
+ 64, 207, 175, 0, 151, 178, 66, 119, 101, 249, 243, 61, 184, 163, 254,
+ 99, 210, 218, 48, 94, 94, 5, 101, 13, 162, 1, 176, 100, 251, 97,
+ 167, 249, 49, 233, 115, 179, 250, 111, 78, 23, 109, 115, 193, 178, 130,
+ 62, 5, 151, 20, 52, 23, 241, 76, 200, 10, 106, 149, 103, 173, 50,
+ 158, 21, 28, 45, 120, 174, 96, 110, 193, 71, 61, 232, 151, 61, 219,
+ 115, 4, 170, 82, 254, 156, 206, 16, 47, 197, 127, 231, 231, 244, 153,
+ 222, 27, 240, 159, 200, 63, 153, 185, 24, 191, 162, 246, 71, 153, 0,
+ 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130
+};
diff --git a/backends/networking/curl/connectionmanager.cpp b/backends/networking/curl/connectionmanager.cpp
new file mode 100644
index 0000000000..f3dc91ad60
--- /dev/null
+++ b/backends/networking/curl/connectionmanager.cpp
@@ -0,0 +1,204 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include "common/system.h"
+#include "common/timer.h"
+#include <curl/curl.h>
+
+namespace Common {
+
+DECLARE_SINGLETON(Networking::ConnectionManager);
+
+}
+
+namespace Networking {
+
+ConnectionManager::ConnectionManager(): _multi(0), _timerStarted(false), _frame(0) {
+ curl_global_init(CURL_GLOBAL_ALL);
+ _multi = curl_multi_init();
+}
+
+ConnectionManager::~ConnectionManager() {
+ stopTimer();
+
+ //terminate all requests
+ _handleMutex.lock();
+ for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end(); ++i) {
+ Request *request = i->request;
+ RequestCallback callback = i->onDeleteCallback;
+ if (request)
+ request->finish();
+ delete request;
+ if (callback)
+ (*callback)(request);
+ }
+ _requests.clear();
+
+ //cleanup
+ curl_multi_cleanup(_multi);
+ curl_global_cleanup();
+ _multi = nullptr;
+ _handleMutex.unlock();
+}
+
+void ConnectionManager::registerEasyHandle(CURL *easy) const {
+ curl_multi_add_handle(_multi, easy);
+}
+
+Request *ConnectionManager::addRequest(Request *request, RequestCallback callback) {
+ _addedRequestsMutex.lock();
+ _addedRequests.push_back(RequestWithCallback(request, callback));
+ if (!_timerStarted)
+ startTimer();
+ _addedRequestsMutex.unlock();
+ return request;
+}
+
+void ConnectionManager::showCloudDisabledIcon() {
+ _icon.showDisabled();
+ startTimer();
+}
+
+Common::String ConnectionManager::urlEncode(Common::String s) const {
+ if (!_multi)
+ return "";
+ char *output = curl_easy_escape(_multi, s.c_str(), s.size());
+ if (output) {
+ Common::String result = output;
+ curl_free(output);
+ return result;
+ }
+ return "";
+}
+
+uint32 ConnectionManager::getCloudRequestsPeriodInMicroseconds() {
+ return TIMER_INTERVAL * CLOUD_PERIOD;
+}
+
+//private goes here:
+
+void connectionsThread(void *ignored) {
+ ConnMan.handle();
+}
+
+void ConnectionManager::startTimer(int interval) {
+ Common::TimerManager *manager = g_system->getTimerManager();
+ if (manager->installTimerProc(connectionsThread, interval, 0, "Networking::ConnectionManager's Timer")) {
+ _timerStarted = true;
+ } else {
+ warning("Failed to install Networking::ConnectionManager's timer");
+ }
+}
+
+void ConnectionManager::stopTimer() {
+ debug(9, "timer stopped");
+ Common::TimerManager *manager = g_system->getTimerManager();
+ manager->removeTimerProc(connectionsThread);
+ _timerStarted = false;
+}
+
+bool ConnectionManager::hasAddedRequests() {
+ _addedRequestsMutex.lock();
+ bool hasNewRequests = !_addedRequests.empty();
+ _addedRequestsMutex.unlock();
+ return hasNewRequests;
+}
+
+void ConnectionManager::handle() {
+ //lock mutex here (in case another handle() would be called before this one ends)
+ _handleMutex.lock();
+ ++_frame;
+ if (_frame % CLOUD_PERIOD == 0)
+ interateRequests();
+ if (_frame % CURL_PERIOD == 0)
+ processTransfers();
+
+ if (_icon.draw() && _requests.empty() && !hasAddedRequests())
+ stopTimer();
+ _handleMutex.unlock();
+}
+
+void ConnectionManager::interateRequests() {
+ //add new requests
+ _addedRequestsMutex.lock();
+ for (Common::Array<RequestWithCallback>::iterator i = _addedRequests.begin(); i != _addedRequests.end(); ++i) {
+ _requests.push_back(*i);
+ }
+ _addedRequests.clear();
+ _addedRequestsMutex.unlock();
+
+ //call handle() of all running requests (so they can do their work)
+ debug(9, "handling %d request(s)", _requests.size());
+ for (Common::Array<RequestWithCallback>::iterator i = _requests.begin(); i != _requests.end();) {
+ Request *request = i->request;
+ if (request) {
+ if (request->state() == PROCESSING)
+ request->handle();
+ else if (request->state() == RETRY)
+ request->handleRetry();
+ }
+
+ if (!request || request->state() == FINISHED) {
+ delete (i->request);
+ if (i->onDeleteCallback)
+ (*i->onDeleteCallback)(i->request); //that's not a mistake (we're passing an address and that method knows there is no object anymore)
+ _requests.erase(i);
+ continue;
+ }
+
+ ++i;
+ }
+}
+
+void ConnectionManager::processTransfers() {
+ if (!_multi) return;
+
+ //check libcurl's transfers and notify requests of messages from queue (transfer completion or failure)
+ int transfersRunning;
+ curl_multi_perform(_multi, &transfersRunning);
+
+ int messagesInQueue;
+ CURLMsg *curlMsg;
+ while ((curlMsg = curl_multi_info_read(_multi, &messagesInQueue))) {
+ CURL *easyHandle = curlMsg->easy_handle;
+
+ NetworkReadStream *stream;
+ curl_easy_getinfo(easyHandle, CURLINFO_PRIVATE, &stream);
+ if (stream)
+ stream->finished();
+
+ if (curlMsg->msg == CURLMSG_DONE) {
+ debug(9, "ConnectionManager: SUCCESS (%d - %s)", curlMsg->data.result, curl_easy_strerror(curlMsg->data.result));
+ } else {
+ warning("ConnectionManager: FAILURE (CURLMsg (%d))", curlMsg->msg);
+ }
+
+ curl_multi_remove_handle(_multi, easyHandle);
+ }
+}
+
+} // End of namespace Cloud
diff --git a/backends/networking/curl/connectionmanager.h b/backends/networking/curl/connectionmanager.h
new file mode 100644
index 0000000000..826bef6d36
--- /dev/null
+++ b/backends/networking/curl/connectionmanager.h
@@ -0,0 +1,132 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
+#define BACKENDS_NETWORKING_CURL_CONNECTIONMANAGER_H
+
+#include "backends/networking/curl/cloudicon.h"
+#include "backends/networking/curl/request.h"
+#include "common/str.h"
+#include "common/singleton.h"
+#include "common/hashmap.h"
+#include "common/mutex.h"
+
+typedef void CURL;
+typedef void CURLM;
+struct curl_slist;
+
+namespace Networking {
+
+class NetworkReadStream;
+
+class ConnectionManager : public Common::Singleton<ConnectionManager> {
+ static const uint32 FRAMES_PER_SECOND = 20;
+ static const uint32 TIMER_INTERVAL = 1000000 / FRAMES_PER_SECOND;
+ static const uint32 CLOUD_PERIOD = 20; //every 20th frame
+ static const uint32 CURL_PERIOD = 1; //every frame
+
+ friend void connectionsThread(void *); //calls handle()
+
+ typedef Common::BaseCallback<Request *> *RequestCallback;
+
+ /**
+ * RequestWithCallback is used by ConnectionManager to
+ * storage the Request and a callback which should be
+ * called on Request delete.
+ *
+ * Usually one won't need to pass such callback, but
+ * in some cases you'd like to know whether Request is
+ * still running.
+ *
+ * For example, Cloud::Storage is keeping track of how
+ * many Requests are running, and thus it needs to know
+ * that Request was destroyed to decrease its counter.
+ *
+ * onDeleteCallback is called with *invalid* pointer.
+ * ConnectionManager deletes Request first and then passes
+ * the pointer to the callback. One may use the address
+ * to find it in own HashMap or Array and remove it.
+ * So, again, this pointer is for information only. One
+ * cannot use it.
+ */
+ struct RequestWithCallback {
+ Request *request;
+ RequestCallback onDeleteCallback;
+
+ RequestWithCallback(Request *rq = nullptr, RequestCallback cb = nullptr): request(rq), onDeleteCallback(cb) {}
+ };
+
+ CURLM *_multi;
+ bool _timerStarted;
+ Common::Array<RequestWithCallback> _requests, _addedRequests;
+ Common::Mutex _handleMutex, _addedRequestsMutex;
+ CloudIcon _icon;
+ uint32 _frame;
+
+ void startTimer(int interval = TIMER_INTERVAL);
+ void stopTimer();
+ void handle();
+ void interateRequests();
+ void processTransfers();
+ bool hasAddedRequests();
+
+public:
+ ConnectionManager();
+ virtual ~ConnectionManager();
+
+ /**
+ * All libcurl transfers are going through this ConnectionManager.
+ * So, if you want to start any libcurl transfer, you must create
+ * an easy handle and register it using this method.
+ */
+ void registerEasyHandle(CURL *easy) const;
+
+ /**
+ * Use this method to add new Request into manager's queue.
+ * Manager will periodically call handle() method of these
+ * Requests until they set their state to FINISHED.
+ *
+ * If Request's state is RETRY, handleRetry() is called instead.
+ *
+ * The passed callback would be called after Request is deleted.
+ *
+ * @note This method starts the timer if it's not started yet.
+ *
+ * @return the same Request pointer, just as a shortcut
+ */
+ Request *addRequest(Request *request, RequestCallback callback = nullptr);
+
+ /** Shows a "cloud disabled" icon for a three seconds. */
+ void showCloudDisabledIcon();
+
+ /** Return URL-encoded version of given string. */
+ Common::String urlEncode(Common::String s) const;
+
+ static uint32 getCloudRequestsPeriodInMicroseconds();
+};
+
+/** Shortcut for accessing the connection manager. */
+#define ConnMan Networking::ConnectionManager::instance()
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/curl/curljsonrequest.cpp b/backends/networking/curl/curljsonrequest.cpp
new file mode 100644
index 0000000000..1899cbd913
--- /dev/null
+++ b/backends/networking/curl/curljsonrequest.cpp
@@ -0,0 +1,215 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/curljsonrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/debug.h"
+#include "common/json.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+CurlJsonRequest::CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url) :
+ CurlRequest(nullptr, ecb, url), _jsonCallback(cb), _contentsStream(DisposeAfterUse::YES),
+ _buffer(new byte[CURL_JSON_REQUEST_BUFFER_SIZE]) {}
+
+CurlJsonRequest::~CurlJsonRequest() {
+ delete _jsonCallback;
+ delete[] _buffer;
+}
+
+char *CurlJsonRequest::getPreparedContents() {
+ //write one more byte in the end
+ byte zero[1] = {0};
+ _contentsStream.write(zero, 1);
+
+ //replace all "bad" bytes with '.' character
+ byte *result = _contentsStream.getData();
+ uint32 size = _contentsStream.size();
+ for (uint32 i = 0; i < size; ++i) {
+ if (result[i] == '\n')
+ result[i] = ' '; //yeah, kinda stupid
+ else if (result[i] < 0x20 || result[i] > 0x7f)
+ result[i] = '.';
+ }
+
+ //make it zero-terminated string
+ result[size - 1] = '\0';
+
+ return (char *)result;
+}
+
+void CurlJsonRequest::handle() {
+ if (!_stream) _stream = makeStream();
+
+ if (_stream) {
+ uint32 readBytes = _stream->read(_buffer, CURL_JSON_REQUEST_BUFFER_SIZE);
+ if (readBytes != 0)
+ if (_contentsStream.write(_buffer, readBytes) != readBytes)
+ warning("CurlJsonRequest: unable to write all the bytes into MemoryWriteStreamDynamic");
+
+ if (_stream->eos()) {
+ char *contents = getPreparedContents();
+ Common::JSONValue *json = Common::JSON::parse(contents);
+ if (json) {
+ finishJson(json); //it's JSON even if's not 200 OK? That's fine!..
+ } else {
+ if (_stream->httpResponseCode() == 200) //no JSON, but 200 OK? That's fine!..
+ finishJson(nullptr);
+ else
+ finishError(ErrorResponse(this, false, true, contents, _stream->httpResponseCode()));
+ }
+ }
+ }
+}
+
+void CurlJsonRequest::restart() {
+ if (_stream)
+ delete _stream;
+ _stream = nullptr;
+ _contentsStream = Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
+ //with no stream available next handle() will create another one
+}
+
+void CurlJsonRequest::finishJson(Common::JSONValue *json) {
+ Request::finishSuccess();
+ if (_jsonCallback)
+ (*_jsonCallback)(JsonResponse(this, json)); //potential memory leak, free it in your callbacks!
+ else
+ delete json;
+}
+
+bool CurlJsonRequest::jsonIsObject(Common::JSONValue *item, const char *warningPrefix) {
+ if (item == nullptr) {
+ warning("%s: passed item is NULL", warningPrefix);
+ return false;
+ }
+
+ if (item->isObject()) return true;
+
+ warning("%s: passed item is not an object", warningPrefix);
+ debug(9, "%s", item->stringify(true).c_str());
+ return false;
+}
+
+bool CurlJsonRequest::jsonContainsObject(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+ if (!item.contains(key)) {
+ if (isOptional) {
+ return true;
+ }
+
+ warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
+ return false;
+ }
+
+ if (item.getVal(key)->isObject()) return true;
+
+ warning("%s: passed item's \"%s\" attribute is not an object", warningPrefix, key);
+ debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+ return false;
+}
+
+bool CurlJsonRequest::jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+ if (!item.contains(key)) {
+ if (isOptional) {
+ return true;
+ }
+
+ warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
+ return false;
+ }
+
+ if (item.getVal(key)->isString()) return true;
+
+ warning("%s: passed item's \"%s\" attribute is not a string", warningPrefix, key);
+ debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+ return false;
+}
+
+bool CurlJsonRequest::jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+ if (!item.contains(key)) {
+ if (isOptional) {
+ return true;
+ }
+
+ warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
+ return false;
+ }
+
+ if (item.getVal(key)->isIntegerNumber()) return true;
+
+ warning("%s: passed item's \"%s\" attribute is not an integer", warningPrefix, key);
+ debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+ return false;
+}
+
+bool CurlJsonRequest::jsonContainsArray(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+ if (!item.contains(key)) {
+ if (isOptional) {
+ return true;
+ }
+
+ warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
+ return false;
+ }
+
+ if (item.getVal(key)->isArray()) return true;
+
+ warning("%s: passed item's \"%s\" attribute is not an array", warningPrefix, key);
+ debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+ return false;
+}
+
+bool CurlJsonRequest::jsonContainsStringOrIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+ if (!item.contains(key)) {
+ if (isOptional) {
+ return true;
+ }
+
+ warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
+ return false;
+ }
+
+ if (item.getVal(key)->isString() || item.getVal(key)->isIntegerNumber()) return true;
+
+ warning("%s: passed item's \"%s\" attribute is neither a string or an integer", warningPrefix, key);
+ debug(9, "%s", item.getVal(key)->stringify(true).c_str());
+ return false;
+}
+
+bool CurlJsonRequest::jsonContainsAttribute(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional) {
+ if (!item.contains(key)) {
+ if (isOptional) {
+ return true;
+ }
+
+ warning("%s: passed item misses the \"%s\" attribute", warningPrefix, key);
+ return false;
+ }
+
+ return true;
+}
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/curljsonrequest.h b/backends/networking/curl/curljsonrequest.h
new file mode 100644
index 0000000000..edd523015a
--- /dev/null
+++ b/backends/networking/curl/curljsonrequest.h
@@ -0,0 +1,67 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
+#define BACKENDS_NETWORKING_CURL_CURLJSONREQUEST_H
+
+#include "backends/networking/curl/curlrequest.h"
+#include "common/memstream.h"
+#include "common/json.h"
+
+namespace Networking {
+
+typedef Response<Common::JSONValue *> JsonResponse;
+typedef Common::BaseCallback<JsonResponse> *JsonCallback;
+
+#define CURL_JSON_REQUEST_BUFFER_SIZE 512 * 1024
+
+class CurlJsonRequest: public CurlRequest {
+protected:
+ JsonCallback _jsonCallback;
+ Common::MemoryWriteStreamDynamic _contentsStream;
+ byte *_buffer;
+
+ /** Prepares raw bytes from _contentsStream to be parsed with Common::JSON::parse(). */
+ char *getPreparedContents();
+
+ /** Sets FINISHED state and passes the JSONValue * into user's callback in JsonResponse. */
+ virtual void finishJson(Common::JSONValue *json);
+
+public:
+ CurlJsonRequest(JsonCallback cb, ErrorCallback ecb, Common::String url);
+ virtual ~CurlJsonRequest();
+
+ virtual void handle();
+ virtual void restart();
+
+ static bool jsonIsObject(Common::JSONValue *item, const char *warningPrefix);
+ static bool jsonContainsObject(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+ static bool jsonContainsString(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+ static bool jsonContainsIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+ static bool jsonContainsArray(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+ static bool jsonContainsStringOrIntegerNumber(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+ static bool jsonContainsAttribute(Common::JSONObject &item, const char *key, const char *warningPrefix, bool isOptional = false);
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/curl/curlrequest.cpp b/backends/networking/curl/curlrequest.cpp
new file mode 100644
index 0000000000..64fa347023
--- /dev/null
+++ b/backends/networking/curl/curlrequest.cpp
@@ -0,0 +1,162 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/curlrequest.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "backends/networking/curl/networkreadstream.h"
+#include "common/textconsole.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+CurlRequest::CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url):
+ Request(cb, ecb), _url(url), _stream(nullptr), _headersList(nullptr), _bytesBuffer(nullptr),
+ _bytesBufferSize(0), _uploading(false), _usingPatch(false) {}
+
+CurlRequest::~CurlRequest() {
+ delete _stream;
+ delete _bytesBuffer;
+}
+
+NetworkReadStream *CurlRequest::makeStream() {
+ if (_bytesBuffer)
+ return new NetworkReadStream(_url.c_str(), _headersList, _bytesBuffer, _bytesBufferSize, _uploading, _usingPatch, true);
+ if (!_formFields.empty() || !_formFiles.empty())
+ return new NetworkReadStream(_url.c_str(), _headersList, _formFields, _formFiles);
+ return new NetworkReadStream(_url.c_str(), _headersList, _postFields, _uploading, _usingPatch);
+}
+
+void CurlRequest::handle() {
+ if (!_stream) _stream = makeStream();
+
+ if (_stream && _stream->eos()) {
+ if (_stream->httpResponseCode() != 200) {
+ warning("CurlRequest: HTTP response code is not 200 OK (it's %ld)", _stream->httpResponseCode());
+ ErrorResponse error(this, false, true, "", _stream->httpResponseCode());
+ finishError(error);
+ return;
+ }
+
+ finishSuccess(); //note that this Request doesn't call its callback on success (that's because it has nothing to return)
+ }
+}
+
+void CurlRequest::restart() {
+ if (_stream)
+ delete _stream;
+ _stream = nullptr;
+ //with no stream available next handle() will create another one
+}
+
+Common::String CurlRequest::date() const {
+ if (_stream) {
+ Common::String headers = _stream->responseHeaders();
+ const char *cstr = headers.c_str();
+ const char *position = strstr(cstr, "Date: ");
+
+ if (position) {
+ Common::String result = "";
+ char c;
+ for (const char *i = position + 6; c = *i, c != 0; ++i) {
+ if (c == '\n' || c == '\r')
+ break;
+ result += c;
+ }
+ return result;
+ }
+ }
+ return "";
+}
+
+void CurlRequest::setHeaders(Common::Array<Common::String> &headers) {
+ curl_slist_free_all(_headersList);
+ _headersList = nullptr;
+ for (uint32 i = 0; i < headers.size(); ++i)
+ addHeader(headers[i]);
+}
+
+void CurlRequest::addHeader(Common::String header) {
+ _headersList = curl_slist_append(_headersList, header.c_str());
+}
+
+void CurlRequest::addPostField(Common::String keyValuePair) {
+ if (_bytesBuffer)
+ warning("CurlRequest: added POST fields would be ignored, because there is buffer present");
+
+ if (!_formFields.empty() || !_formFiles.empty())
+ warning("CurlRequest: added POST fields would be ignored, because there are form fields/files present");
+
+ if (_postFields == "")
+ _postFields = keyValuePair;
+ else
+ _postFields += "&" + keyValuePair;
+}
+
+void CurlRequest::addFormField(Common::String name, Common::String value) {
+ if (_bytesBuffer)
+ warning("CurlRequest: added POST form fields would be ignored, because there is buffer present");
+
+ if (_formFields.contains(name))
+ warning("CurlRequest: form field '%s' already had a value", name.c_str());
+
+ _formFields[name] = value;
+}
+
+void CurlRequest::addFormFile(Common::String name, Common::String filename) {
+ if (_bytesBuffer)
+ warning("CurlRequest: added POST form files would be ignored, because there is buffer present");
+
+ if (_formFields.contains(name))
+ warning("CurlRequest: form file field '%s' already had a value", name.c_str());
+
+ _formFiles[name] = filename;
+}
+
+void CurlRequest::setBuffer(byte *buffer, uint32 size) {
+ if (_postFields != "")
+ warning("CurlRequest: added POST fields would be ignored, because buffer added");
+
+ if (_bytesBuffer)
+ delete _bytesBuffer;
+
+ _bytesBuffer = buffer;
+ _bytesBufferSize = size;
+}
+
+void CurlRequest::usePut() { _uploading = true; }
+
+void CurlRequest::usePatch() { _usingPatch = true; }
+
+NetworkReadStreamResponse CurlRequest::execute() {
+ if (!_stream) {
+ _stream = makeStream();
+ ConnMan.addRequest(this);
+ }
+
+ return NetworkReadStreamResponse(this, _stream);
+}
+
+const NetworkReadStream *CurlRequest::getNetworkReadStream() const { return _stream; }
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/curlrequest.h b/backends/networking/curl/curlrequest.h
new file mode 100644
index 0000000000..6ce94f8983
--- /dev/null
+++ b/backends/networking/curl/curlrequest.h
@@ -0,0 +1,100 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_CURLREQUEST_H
+#define BACKENDS_NETWORKING_CURL_CURLREQUEST_H
+
+#include "backends/networking/curl/request.h"
+#include "common/str.h"
+#include "common/array.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+struct curl_slist;
+
+namespace Networking {
+
+class NetworkReadStream;
+
+typedef Response<NetworkReadStream *> NetworkReadStreamResponse;
+typedef Common::BaseCallback<NetworkReadStreamResponse> *NetworkReadStreamCallback;
+
+class CurlRequest: public Request {
+protected:
+ Common::String _url;
+ NetworkReadStream *_stream;
+ curl_slist *_headersList;
+ Common::String _postFields;
+ Common::HashMap<Common::String, Common::String> _formFields;
+ Common::HashMap<Common::String, Common::String> _formFiles;
+ byte *_bytesBuffer;
+ uint32 _bytesBufferSize;
+ bool _uploading; //using PUT method
+ bool _usingPatch; //using PATCH method
+
+ virtual NetworkReadStream *makeStream();
+
+public:
+ CurlRequest(DataCallback cb, ErrorCallback ecb, Common::String url);
+ virtual ~CurlRequest();
+
+ virtual void handle();
+ virtual void restart();
+ virtual Common::String date() const;
+
+ /** Replaces all headers with the passed array of headers. */
+ virtual void setHeaders(Common::Array<Common::String> &headers);
+
+ /** Adds a header into headers list. */
+ virtual void addHeader(Common::String header);
+
+ /** Adds a post field (key=value pair). */
+ virtual void addPostField(Common::String field);
+
+ /** Adds a form/multipart field (name, value). */
+ virtual void addFormField(Common::String name, Common::String value);
+
+ /** Adds a form/multipart file (field name, file name). */
+ virtual void addFormFile(Common::String name, Common::String filename);
+
+ /** Sets bytes buffer. */
+ virtual void setBuffer(byte *buffer, uint32 size);
+
+ /** Remembers to use PUT method when it would create NetworkReadStream. */
+ virtual void usePut();
+
+ /** Remembers to use PATCH method when it would create NetworkReadStream. */
+ virtual void usePatch();
+
+ /**
+ * Starts this Request with ConnMan.
+ * @return its NetworkReadStream in NetworkReadStreamResponse.
+ */
+ virtual NetworkReadStreamResponse execute();
+
+ /** Returns Request's NetworkReadStream. */
+ const NetworkReadStream *getNetworkReadStream() const;
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/curl/networkreadstream.cpp b/backends/networking/curl/networkreadstream.cpp
new file mode 100644
index 0000000000..032aef87be
--- /dev/null
+++ b/backends/networking/curl/networkreadstream.cpp
@@ -0,0 +1,257 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#define FORBIDDEN_SYMBOL_ALLOW_ALL
+
+#include "backends/networking/curl/networkreadstream.h"
+#include "backends/networking/curl/connectionmanager.h"
+#include "base/version.h"
+#include <curl/curl.h>
+
+namespace Networking {
+
+static size_t curlDataCallback(char *d, size_t n, size_t l, void *p) {
+ NetworkReadStream *stream = (NetworkReadStream *)p;
+ if (stream)
+ return stream->write(d, n * l);
+ return 0;
+}
+
+static size_t curlReadDataCallback(char *d, size_t n, size_t l, void *p) {
+ NetworkReadStream *stream = (NetworkReadStream *)p;
+ if (stream)
+ return stream->fillWithSendingContents(d, n * l);
+ return 0;
+}
+
+static size_t curlHeadersCallback(char *d, size_t n, size_t l, void *p) {
+ NetworkReadStream *stream = (NetworkReadStream *)p;
+ if (stream)
+ return stream->addResponseHeaders(d, n * l);
+ return 0;
+}
+
+static int curlProgressCallback(void *p, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
+ NetworkReadStream *stream = (NetworkReadStream *)p;
+ if (stream)
+ stream->setProgress(dlnow, dltotal);
+ return 0;
+}
+
+static int curlProgressCallbackOlder(void *p, double dltotal, double dlnow, double ultotal, double ulnow) {
+ // for libcurl older than 7.32.0 (CURLOPT_PROGRESSFUNCTION)
+ return curlProgressCallback(p, (curl_off_t)dltotal, (curl_off_t)dlnow, (curl_off_t)ultotal, (curl_off_t)ulnow);
+}
+
+void NetworkReadStream::init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+ _eos = _requestComplete = false;
+ _sendingContentsBuffer = nullptr;
+ _sendingContentsSize = _sendingContentsPos = 0;
+ _progressDownloaded = _progressTotal = 0;
+
+ _easy = curl_easy_init();
+ curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
+ curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
+ curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
+ curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
+ curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
+ curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
+ curl_easy_setopt(_easy, CURLOPT_URL, url);
+ curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
+ curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
+ curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+ curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
+ curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
+ curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
+ curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
+#if LIBCURL_VERSION_NUM >= 0x072000
+ // CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
+ // CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
+ curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
+ curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
+#endif
+ if (uploading) {
+ curl_easy_setopt(_easy, CURLOPT_UPLOAD, 1L);
+ curl_easy_setopt(_easy, CURLOPT_READDATA, this);
+ curl_easy_setopt(_easy, CURLOPT_READFUNCTION, curlReadDataCallback);
+ _sendingContentsBuffer = buffer;
+ _sendingContentsSize = bufferSize;
+ } else if (usingPatch) {
+ curl_easy_setopt(_easy, CURLOPT_CUSTOMREQUEST, "PATCH");
+ } else {
+ if (post || bufferSize != 0) {
+ curl_easy_setopt(_easy, CURLOPT_POSTFIELDSIZE, bufferSize);
+ curl_easy_setopt(_easy, CURLOPT_COPYPOSTFIELDS, buffer);
+ }
+ }
+ ConnMan.registerEasyHandle(_easy);
+}
+
+void NetworkReadStream::init(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles) {
+ _eos = _requestComplete = false;
+ _sendingContentsBuffer = nullptr;
+ _sendingContentsSize = _sendingContentsPos = 0;
+ _progressDownloaded = _progressTotal = 0;
+
+ _easy = curl_easy_init();
+ curl_easy_setopt(_easy, CURLOPT_WRITEFUNCTION, curlDataCallback);
+ curl_easy_setopt(_easy, CURLOPT_WRITEDATA, this); //so callback can call us
+ curl_easy_setopt(_easy, CURLOPT_PRIVATE, this); //so ConnectionManager can call us when request is complete
+ curl_easy_setopt(_easy, CURLOPT_HEADER, 0L);
+ curl_easy_setopt(_easy, CURLOPT_HEADERDATA, this);
+ curl_easy_setopt(_easy, CURLOPT_HEADERFUNCTION, curlHeadersCallback);
+ curl_easy_setopt(_easy, CURLOPT_URL, url);
+ curl_easy_setopt(_easy, CURLOPT_VERBOSE, 0L);
+ curl_easy_setopt(_easy, CURLOPT_FOLLOWLOCATION, 1L); //probably it's OK to have it always on
+ curl_easy_setopt(_easy, CURLOPT_HTTPHEADER, headersList);
+ curl_easy_setopt(_easy, CURLOPT_USERAGENT, gScummVMFullVersion);
+ curl_easy_setopt(_easy, CURLOPT_NOPROGRESS, 0L);
+ curl_easy_setopt(_easy, CURLOPT_PROGRESSFUNCTION, curlProgressCallbackOlder);
+ curl_easy_setopt(_easy, CURLOPT_PROGRESSDATA, this);
+#if LIBCURL_VERSION_NUM >= 0x072000
+ // CURLOPT_XFERINFOFUNCTION introduced in libcurl 7.32.0
+ // CURLOPT_PROGRESSFUNCTION is used as a backup plan in case older version is used
+ curl_easy_setopt(_easy, CURLOPT_XFERINFOFUNCTION, curlProgressCallback);
+ curl_easy_setopt(_easy, CURLOPT_XFERINFODATA, this);
+#endif
+
+ // set POST multipart upload form fields/files
+ struct curl_httppost *formpost = nullptr;
+ struct curl_httppost *lastptr = nullptr;
+
+ for (Common::HashMap<Common::String, Common::String>::iterator i = formFields.begin(); i != formFields.end(); ++i) {
+ CURLFORMcode code = curl_formadd(
+ &formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, i->_key.c_str(),
+ CURLFORM_COPYCONTENTS, i->_value.c_str(),
+ CURLFORM_END
+ );
+
+ if (code != CURL_FORMADD_OK)
+ warning("NetworkReadStream: field curl_formadd('%s') failed", i->_key.c_str());
+ }
+
+ for (Common::HashMap<Common::String, Common::String>::iterator i = formFiles.begin(); i != formFiles.end(); ++i) {
+ CURLFORMcode code = curl_formadd(
+ &formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, i->_key.c_str(),
+ CURLFORM_FILE, i->_value.c_str(),
+ CURLFORM_END
+ );
+
+ if (code != CURL_FORMADD_OK)
+ warning("NetworkReadStream: file curl_formadd('%s') failed", i->_key.c_str());
+ }
+
+ curl_easy_setopt(_easy, CURLOPT_HTTPPOST, formpost);
+
+ ConnMan.registerEasyHandle(_easy);
+}
+
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading, bool usingPatch) {
+ init(url, headersList, (const byte *)postFields.c_str(), postFields.size(), uploading, usingPatch, false);
+}
+
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles) {
+ init(url, headersList, formFields, formFiles);
+}
+
+NetworkReadStream::NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post) {
+ init(url, headersList, buffer, bufferSize, uploading, usingPatch, post);
+}
+
+NetworkReadStream::~NetworkReadStream() {
+ if (_easy)
+ curl_easy_cleanup(_easy);
+}
+
+bool NetworkReadStream::eos() const {
+ return _eos;
+}
+
+uint32 NetworkReadStream::read(void *dataPtr, uint32 dataSize) {
+ uint32 actuallyRead = MemoryReadWriteStream::read(dataPtr, dataSize);
+
+ if (actuallyRead == 0) {
+ if (_requestComplete)
+ _eos = true;
+ return 0;
+ }
+
+ return actuallyRead;
+}
+
+void NetworkReadStream::finished() {
+ _requestComplete = true;
+}
+
+long NetworkReadStream::httpResponseCode() const {
+ long responseCode = -1;
+ if (_easy)
+ curl_easy_getinfo(_easy, CURLINFO_RESPONSE_CODE, &responseCode);
+ return responseCode;
+}
+
+Common::String NetworkReadStream::currentLocation() const {
+ Common::String result = "";
+ if (_easy) {
+ char *pointer;
+ curl_easy_getinfo(_easy, CURLINFO_EFFECTIVE_URL, &pointer);
+ result = Common::String(pointer);
+ }
+ return result;
+}
+
+Common::String NetworkReadStream::responseHeaders() const {
+ return _responseHeaders;
+}
+
+uint32 NetworkReadStream::fillWithSendingContents(char *bufferToFill, uint32 maxSize) {
+ uint32 size = _sendingContentsSize - _sendingContentsPos;
+ if (size > maxSize)
+ size = maxSize;
+ for (uint32 i = 0; i < size; ++i) {
+ bufferToFill[i] = _sendingContentsBuffer[_sendingContentsPos + i];
+ }
+ _sendingContentsPos += size;
+ return size;
+}
+
+uint32 NetworkReadStream::addResponseHeaders(char *buffer, uint32 size) {
+ _responseHeaders += Common::String(buffer, size);
+ return size;
+}
+
+double NetworkReadStream::getProgress() const {
+ if (_progressTotal < 1)
+ return 0;
+ return (double)_progressDownloaded / (double)_progressTotal;
+}
+
+void NetworkReadStream::setProgress(uint64 downloaded, uint64 total) {
+ _progressDownloaded = downloaded;
+ _progressTotal = total;
+}
+
+} // End of namespace Cloud
diff --git a/backends/networking/curl/networkreadstream.h b/backends/networking/curl/networkreadstream.h
new file mode 100644
index 0000000000..2be6d591cb
--- /dev/null
+++ b/backends/networking/curl/networkreadstream.h
@@ -0,0 +1,142 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H
+#define BACKENDS_NETWORKING_CURL_NETWORKREADSTREAM_H
+
+#include "common/memstream.h"
+#include "common/stream.h"
+#include "common/str.h"
+#include "common/hashmap.h"
+#include "common/hash-str.h"
+
+typedef void CURL;
+struct curl_slist;
+
+namespace Networking {
+
+class NetworkReadStream: public Common::MemoryReadWriteStream {
+ CURL *_easy;
+ bool _eos, _requestComplete;
+ const byte *_sendingContentsBuffer;
+ uint32 _sendingContentsSize;
+ uint32 _sendingContentsPos;
+ Common::String _responseHeaders;
+ uint64 _progressDownloaded, _progressTotal;
+ void init(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading, bool usingPatch, bool post);
+ void init(const char *url, curl_slist *headersList, Common::HashMap<Common::String, Common::String> formFields, Common::HashMap<Common::String, Common::String> formFiles);
+
+public:
+ /** Send <postFields>, using POST by default. */
+ NetworkReadStream(const char *url, curl_slist *headersList, Common::String postFields, bool uploading = false, bool usingPatch = false);
+ /** Send <formFields>, <formFiles>, using POST multipart/form. */
+ NetworkReadStream(
+ const char *url, curl_slist *headersList,
+ Common::HashMap<Common::String, Common::String> formFields,
+ Common::HashMap<Common::String, Common::String> formFiles);
+ /** Send <buffer, using POST by default. */
+ NetworkReadStream(const char *url, curl_slist *headersList, const byte *buffer, uint32 bufferSize, bool uploading = false, bool usingPatch = false, bool post = true);
+ virtual ~NetworkReadStream();
+
+ /**
+ * Returns true if a read failed because the stream end has been reached.
+ * This flag is cleared by clearErr().
+ * For a SeekableReadStream, it is also cleared by a successful seek.
+ *
+ * @note The semantics of any implementation of this method are
+ * supposed to match those of ISO C feof(). In particular, in a stream
+ * with N bytes, reading exactly N bytes from the start should *not*
+ * set eos; only reading *beyond* the available data should set it.
+ */
+ virtual bool eos() const;
+
+ /**
+ * Read data from the stream. Subclasses must implement this
+ * method; all other read methods are implemented using it.
+ *
+ * @note The semantics of any implementation of this method are
+ * supposed to match those of ISO C fread(), in particular where
+ * it concerns setting error and end of file/stream flags.
+ *
+ * @param dataPtr pointer to a buffer into which the data is read
+ * @param dataSize number of bytes to be read
+ * @return the number of bytes which were actually read.
+ */
+ virtual uint32 read(void *dataPtr, uint32 dataSize);
+
+ /**
+ * This method is called by ConnectionManager to indicate
+ * that transfer is finished.
+ *
+ * @note It's called on failure too.
+ */
+ void finished();
+
+ /**
+ * Returns HTTP response code from inner CURL handle.
+ * It returns -1 to indicate there is no inner handle.
+ *
+ * @note This method should be called when eos() == true.
+ */
+ long httpResponseCode() const;
+
+ /**
+ * Return current location URL from inner CURL handle.
+ * "" is returned to indicate there is no inner handle.
+ *
+ * @note This method should be called when eos() == true.
+ */
+ Common::String currentLocation() const;
+
+ /**
+ * Return response headers.
+ *
+ * @note This method should be called when eos() == true.
+ */
+ Common::String responseHeaders() const;
+
+ /**
+ * Fills the passed buffer with _sendingContentsBuffer contents.
+ * It works similarly to read(), expect it's not for reading
+ * Stream's contents, but for sending our own data to the server.
+ *
+ * @returns how many bytes were actually read (filled in)
+ */
+ uint32 fillWithSendingContents(char *bufferToFill, uint32 maxSize);
+
+ /**
+ * Remembers headers returned to CURL in server's response.
+ *
+ * @returns how many bytes were actually read
+ */
+ uint32 addResponseHeaders(char *buffer, uint32 size);
+
+ /** Returns a number in range [0, 1], where 1 is "complete". */
+ double getProgress() const;
+
+ /** Used in curl progress callback to pass current downloaded/total values. */
+ void setProgress(uint64 downloaded, uint64 total);
+};
+
+} // End of namespace Networking
+
+#endif
diff --git a/backends/networking/curl/request.cpp b/backends/networking/curl/request.cpp
new file mode 100644
index 0000000000..30af48a478
--- /dev/null
+++ b/backends/networking/curl/request.cpp
@@ -0,0 +1,74 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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 "backends/networking/curl/request.h"
+
+namespace Networking {
+
+ErrorResponse::ErrorResponse(Request *rq):
+ request(rq), interrupted(false), failed(true), response(""), httpResponseCode(-1) {}
+
+ErrorResponse::ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode):
+ request(rq), interrupted(interrupt), failed(failure), response(resp), httpResponseCode(httpCode) {}
+
+Request::Request(DataCallback cb, ErrorCallback ecb):
+ _callback(cb), _errorCallback(ecb), _state(PROCESSING), _retryInSeconds(0) {}
+
+Request::~Request() {
+ delete _callback;
+ delete _errorCallback;
+}
+
+void Request::handleRetry() {
+ if (_retryInSeconds > 0) {
+ --_retryInSeconds;
+ } else {
+ _state = PROCESSING;
+ restart();
+ }
+}
+
+void Request::pause() { _state = PAUSED; }
+
+void Request::finish() {
+ ErrorResponse error(this, true, false, "", -1);
+ finishError(error);
+}
+
+void Request::retry(uint32 seconds) {
+ _state = RETRY;
+ _retryInSeconds = seconds;
+}
+
+RequestState Request::state() const { return _state; }
+
+Common::String Request::date() const { return ""; }
+
+void Request::finishError(ErrorResponse error) {
+ _state = FINISHED;
+ if (_errorCallback)
+ (*_errorCallback)(error);
+}
+
+void Request::finishSuccess() { _state = FINISHED; }
+
+} // End of namespace Networking
diff --git a/backends/networking/curl/request.h b/backends/networking/curl/request.h
new file mode 100644
index 0000000000..9b366ea40c
--- /dev/null
+++ b/backends/networking/curl/request.h
@@ -0,0 +1,203 @@
+/* ScummVM - Graphic Adventure Engine
+*
+* ScummVM is the legal property of its developers, whose names
+* are too numerous to list here. Please refer to the COPYRIGHT
+* file distributed with this source distribution.
+*
+* 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.
+*
+*/
+
+#ifndef BACKENDS_NETWORKING_CURL_REQUEST_H
+#define BACKENDS_NETWORKING_CURL_REQUEST_H
+
+#include "common/callback.h"
+#include "common/scummsys.h"
+#include "common/str.h"
+
+namespace Networking {
+
+class Request;
+
+/**
+ * Response<T> is a struct to be returned from Request
+ * to user's callbacks. It's a type safe way to indicate
+ * which "return value" Request has and user awaits.
+ *
+ * It just keeps a Request pointer together with
+ * some T value (which might be a pointer, a reference
+ * or a plain type (copied by value)).
+ *
+ * To make it more convenient, typedefs are used.
+ * For example, Response<void *> is called DataResponse
+ * and corresponding callback pointer is DataCallback.
+ */
+
+template<typename T> struct Response {
+ Request *request;
+ T value;
+
+ Response(Request *rq, T v) : request(rq), value(v) {}
+};
+
+/**
+ * ErrorResponse is a struct to be returned from Request
+ * to user's failure callbacks.
+ *
+ * It keeps a Request pointer together with some useful
+ * information fields, which would explain why failure
+ * callback was called.
+ *
+ * <interrupted> flag is set when Request was interrupted,
+ * i.e. finished by user with finish() call.
+ *
+ * <failed> flag is set when Request has failed because of
+ * some error (bad server response, for example).
+ *
+ * <response> contains server's original response.
+ *
+ * <httpResponseCode> contains server's HTTP response code.
+ */
+
+struct ErrorResponse {
+ Request *request;
+ bool interrupted;
+ bool failed;
+ Common::String response;
+ long httpResponseCode;
+
+ ErrorResponse(Request *rq);
+ ErrorResponse(Request *rq, bool interrupt, bool failure, Common::String resp, long httpCode);
+};
+
+typedef Response<void *> DataReponse;
+typedef Common::BaseCallback<DataReponse> *DataCallback;
+typedef Common::BaseCallback<ErrorResponse> *ErrorCallback;
+
+/**
+ * RequestState is used to indicate current Request state.
+ * ConnectionManager uses it to decide what to do with the Request.
+ *
+ * PROCESSING state indicates that Request is working.
+ * ConnectionManager calls handle() method of Requests in that state.
+ *
+ * PAUSED state indicates that Request is not working.
+ * ConnectionManager keeps Requests in that state and doesn't call any methods of those.
+ *
+ * RETRY state indicates that Request must restart after a few seconds.
+ * ConnectionManager calls handleRetry() method of Requests in that state.
+ * Default handleRetry() implementation decreases _retryInSeconds value
+ * until it reaches zero. When it does, Request's restart() method is called.
+ *
+ * FINISHED state indicates that Request did the work and might be deleted.
+ * ConnectionManager deletes Requests in that state.
+ * After this state is set, but before ConnectionManager deletes the Request,
+ * Request calls user's callback. User can ask Request to change its state
+ * by calling retry() or pause() methods and Request won't be deleted.
+ *
+ * Request get a success and failure callbacks. Request must call one
+ * (and only one!) of these callbacks when it sets FINISHED state.
+ */
+enum RequestState {
+ PROCESSING,
+ PAUSED,
+ RETRY,
+ FINISHED
+};
+
+class Request {
+protected:
+ /**
+ * Callback, which should be called when Request is finished.
+ * That's the way Requests pass the result to the code which asked to create this request.
+ *
+ * @note some Requests use their own callbacks to return something but void *.
+ * @note callback must be called in finish() or similar method.
+ */
+ DataCallback _callback;
+
+ /**
+ * Callback, which should be called when Request is failed/interrupted.
+ * That's the way Requests pass error information to the code which asked to create this request.
+ * @note callback must be called in finish() or similar method.
+ */
+ ErrorCallback _errorCallback;
+
+ /**
+ * Request state, which is used by ConnectionManager to determine
+ * whether request might be deleted or it's still working.
+ *
+ * State might be changed from outside with finish(), pause() or
+ * retry() methods. Override these if you want to react to these
+ * changes correctly.
+ */
+ RequestState _state;
+
+ /** In RETRY state this indicates whether it's time to call restart(). */
+ uint32 _retryInSeconds;
+
+ /** Sets FINISHED state and calls the _errorCallback with given error. */
+ virtual void finishError(ErrorResponse error);
+
+ /** Sets FINISHED state. Implementations might extend it if needed. */
+ virtual void finishSuccess();
+
+public:
+ Request(DataCallback cb, ErrorCallback ecb);
+ virtual ~Request();
+
+ /** Method, which does actual work. Depends on what this Request is doing. */
+ virtual void handle() = 0;
+
+ /** Method, which is called by ConnectionManager when Request's state is RETRY. */
+ virtual void handleRetry();
+
+ /** Method, which is used to restart the Request. */
+ virtual void restart() = 0;
+
+ /** Method, which is called to pause the Request. */
+ virtual void pause();
+
+ /**
+ * Method, which is called to *interrupt* the Request.
+ * When it's called, Request must stop its work and
+ * call the failure callback to notify user.
+ */
+ virtual void finish();
+
+ /** Method, which is called to retry the Request. */
+ virtual void retry(uint32 seconds);
+
+ /** Returns Request's current state. */
+ RequestState state() const;
+
+ /**
+ * Return date this Request received from server.
+ * It could be extracted from "Date" header,
+ * which is kept in NetworkReadStream.
+ *
+ * @note not all Requests do that, so "" is returned
+ * to indicate the date is unknown. That's also true
+ * if no server response available or no "Date" header
+ * was passed.
+ *
+ * @returns date from "Date" response header.
+ */
+ virtual Common::String date() const;
+};
+
+} // End of namespace Networking
+
+#endif