3 // SuperTux - Add-on Manager
4 // Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.de>
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 #include "addon_manager.hpp"
32 #include <curl/curl.h>
33 #include <curl/types.h>
34 #include <curl/easy.h>
40 size_t my_curl_string_append(void *ptr, size_t size, size_t nmemb, void *string_ptr)
42 std::string& s = *static_cast<std::string*>(string_ptr);
43 std::string buf(static_cast<char*>(ptr), size * nmemb);
45 log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
49 size_t my_curl_physfs_write(void *ptr, size_t size, size_t nmemb, void *f_p)
51 PHYSFS_file* f = static_cast<PHYSFS_file*>(f_p);
52 PHYSFS_sint64 written = PHYSFS_write(f, ptr, size, nmemb);
53 log_debug << "read " << size * nmemb << " bytes of data..." << std::endl;
54 return size * written;
61 AddonManager::get_instance()
63 static AddonManager instance;
67 AddonManager::AddonManager()
70 curl_global_init(CURL_GLOBAL_ALL);
74 AddonManager::~AddonManager()
77 curl_global_cleanup();
82 AddonManager::get_installed_addons() const
84 std::vector<Addon> addons;
86 // iterate over complete search path (i.e. directories and archives)
87 char **i = PHYSFS_getSearchPath();
88 if (!i) throw std::runtime_error("Could not query physfs search path");
89 for (; *i != NULL; i++) {
91 // get filename of potential archive
92 std::string fileName = *i;
94 // make sure it's in the writeDir
95 static const std::string writeDir = PHYSFS_getWriteDir();
96 if (fileName.compare(0, writeDir.length(), writeDir) != 0) continue;
98 // make sure it looks like an archive
99 static const std::string archiveExt = ".zip";
100 if (fileName.compare(fileName.length()-archiveExt.length(), archiveExt.length(), archiveExt) != 0) continue;
102 // make sure it exists
104 if (stat(fileName.c_str(), &stats) != 0) continue;
106 // make sure it's an actual file
107 if (!S_ISREG(stats.st_mode)) continue;
109 // extract nice title
110 static const char* dirSep = PHYSFS_getDirSeparator();
111 std::string::size_type n = fileName.rfind(dirSep) + 1;
112 if (n == std::string::npos) n = 0;
113 std::string title = fileName.substr(n, fileName.length() - n - archiveExt.length());
117 addon.fname = fileName;
118 addon.isInstalled = true;
120 addons.push_back(addon);
127 AddonManager::get_available_addons() const
129 std::vector<Addon> addons;
133 // FIXME: This URL is just for testing!
134 const char* baseUrl = "http://www.deltadevelopment.de/users/christoph/supertux/addons/";
135 std::string html = "";
138 curl_handle = curl_easy_init();
139 curl_easy_setopt(curl_handle, CURLOPT_URL, baseUrl);
140 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
141 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_string_append);
142 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &html);
143 curl_easy_perform(curl_handle);
144 curl_easy_cleanup(curl_handle);
146 //std::string html = "Blubb<a href=\"http://www.deltadevelopment.de/users/christoph/supertux/addons/coconut_fortress.zip\">Coconut Fortress</a>\nFoobar<a href=\"http://www.deltadevelopment.de/users/christoph/supertux/addons/in_the_spring.zip\">Another</a>Baz";
147 static const std::string startToken = "href=\"";
148 static const std::string endToken = "\"";
150 // extract urls: for each startToken found...
151 std::string::size_type n = 0;
152 while ((n = html.find(startToken)) != std::string::npos) {
154 // strip everything up to and including token
155 html.erase(0, n + startToken.length());
158 std::string::size_type n2 = html.find(endToken);
159 if (n2 == std::string::npos) break;
161 // extract url: it's the string inbetween
162 std::string url = html.substr(0, n2);
164 // strip everything up to and including endToken
165 html.erase(0, n2 + endToken.length());
168 url = std::string(baseUrl) + url;
170 // make sure url looks like it points to an archive
171 static const std::string archiveExt = ".zip";
172 if (url.compare(url.length()-archiveExt.length(), archiveExt.length(), archiveExt) != 0) continue;
174 // extract nice title
175 std::string::size_type n = url.rfind('/') + 1;
176 if (n == std::string::npos) n = 0;
177 std::string title = url.substr(n, url.length() - n - archiveExt.length());
179 // construct file name
180 std::string fname = url.substr(n);
182 // make sure it does not contain weird characters
183 if (fname.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) continue;
189 addon.isInstalled = false;
191 addons.push_back(addon);
200 AddonManager::install(const Addon& addon)
205 char* url = (char*)malloc(addon.url.length() + 1);
206 strncpy(url, addon.url.c_str(), addon.url.length() + 1);
208 PHYSFS_file* f = PHYSFS_openWrite(addon.fname.c_str());
210 log_debug << "Downloading \"" << url << "\"" << std::endl;
213 curl_handle = curl_easy_init();
214 curl_easy_setopt(curl_handle, CURLOPT_URL, url);
215 curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
216 curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_physfs_write);
217 curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, f);
218 curl_easy_perform(curl_handle);
219 curl_easy_cleanup(curl_handle);
225 static const std::string writeDir = PHYSFS_getWriteDir();
226 static const std::string dirSep = PHYSFS_getDirSeparator();
227 std::string fullFilename = writeDir + dirSep + addon.fname;
228 log_debug << "Finished downloading \"" << fullFilename << "\"" << std::endl;
229 PHYSFS_addToSearchPath(fullFilename.c_str(), 1);
237 AddonManager::remove(const Addon& addon)
239 PHYSFS_removeFromSearchPath(addon.fname.c_str());
240 PHYSFS_delete(addon.fname.c_str());