diff options
Diffstat (limited to 'backends/networking/curl')
-rw-r--r-- | backends/networking/curl/cloudicon.cpp | 171 | ||||
-rw-r--r-- | backends/networking/curl/cloudicon.h | 70 | ||||
-rw-r--r-- | backends/networking/curl/cloudicon_data.h | 111 | ||||
-rw-r--r-- | backends/networking/curl/cloudicon_disabled_data.h | 117 | ||||
-rw-r--r-- | backends/networking/curl/connectionmanager.cpp | 204 | ||||
-rw-r--r-- | backends/networking/curl/connectionmanager.h | 132 | ||||
-rw-r--r-- | backends/networking/curl/curljsonrequest.cpp | 215 | ||||
-rw-r--r-- | backends/networking/curl/curljsonrequest.h | 67 | ||||
-rw-r--r-- | backends/networking/curl/curlrequest.cpp | 162 | ||||
-rw-r--r-- | backends/networking/curl/curlrequest.h | 100 | ||||
-rw-r--r-- | backends/networking/curl/networkreadstream.cpp | 257 | ||||
-rw-r--r-- | backends/networking/curl/networkreadstream.h | 142 | ||||
-rw-r--r-- | backends/networking/curl/request.cpp | 74 | ||||
-rw-r--r-- | backends/networking/curl/request.h | 203 |
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 |