Large scale refactor/rewrite of the AddonManager, adding cleaner separation between...
[supertux.git] / src / addon / downloader.cpp
1 //  SuperTux
2 //  Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "addon/downloader.hpp"
18
19 #include <curl/curl.h>
20 #include <curl/easy.h>
21 #include <physfs.h>
22 #include <memory>
23 #include <stdexcept>
24
25 #include "util/log.hpp"
26 #include "version.h"
27
28 namespace {
29
30 size_t my_curl_string_append(void* ptr, size_t size, size_t nmemb, void* userdata)
31 {
32   std::string& s = *static_cast<std::string*>(userdata);
33   std::string buf(static_cast<char*>(ptr), size * nmemb);
34   s += buf;
35   log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
36   return size * nmemb;
37 }
38
39 size_t my_curl_physfs_write(void* ptr, size_t size, size_t nmemb, void* userdata)
40 {
41   PHYSFS_file* f = static_cast<PHYSFS_file*>(userdata);
42   PHYSFS_sint64 written = PHYSFS_write(f, ptr, size, nmemb);
43   log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
44   return size * written;
45 }
46
47 } // namespace
48
49 Downloader::Downloader()
50 {
51   curl_global_init(CURL_GLOBAL_ALL);
52 }
53
54 Downloader::~Downloader()
55 {
56   curl_global_cleanup();
57 }
58
59 void
60 Downloader::download(const std::string& url,
61                      size_t (*write_func)(void* ptr, size_t size, size_t nmemb, void* userdata),
62                      void* userdata)
63 {
64   log_info << "Downloading " << url << std::endl;
65
66   char error_buffer[CURL_ERROR_SIZE+1];
67
68   CURL* curl_handle = curl_easy_init();
69   curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
70   curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
71   curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_func);
72   curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, userdata);
73   curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
74   curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
75   curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
76   curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
77   curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
78   CURLcode result = curl_easy_perform(curl_handle);
79   curl_easy_cleanup(curl_handle);
80
81   if (result != CURLE_OK)
82   {
83     std::string why = error_buffer[0] ? error_buffer : "unhandled error";
84     throw std::runtime_error(url + ": download failed: " + why);
85   }
86 }
87
88 std::string
89 Downloader::download(const std::string& url)
90 {
91   std::string result;
92   download(url, my_curl_string_append, &result);
93   return result;
94 }
95
96 void
97 Downloader::download(const std::string& url, const std::string& filename)
98 {
99   std::unique_ptr<PHYSFS_file, int(*)(PHYSFS_File*)> fout(PHYSFS_openWrite(filename.c_str()),
100                                                           PHYSFS_close);
101   download(url, my_curl_physfs_write, fout.get());
102 }
103
104 /* EOF */