AddonManager Class Reference

Checks for, installs and removes Add-ons. More...

#include <addon_manager.hpp>

List of all members.

Public Member Functions

std::vector< Addon * > get_addons ()
 returns a list of installed Add-ons
void check_online ()
 downloads list of available Add-ons
void install (Addon *addon)
 Download and install Add-on.
void remove (Addon *addon)
 Physically delete Add-on.
void disable (Addon *addon)
 Unload Add-on and mark as not to be loaded automatically.
void enable (Addon *addon)
 Load Add-on and mark as to be loaded automatically.
void unload (Addon *addon)
 Remove Add-on from search path.
void load (Addon *addon)
 Add Add-on to search path.
void load_addons ()
 Loads all enabled Add-ons, i.e.
void write (Writer &writer)
 Write AddonManager configuration to Lisp.
void read (const Reader &lisp)
 Read AddonManager configuration from Lisp.

Static Public Member Functions

static AddonManagerget_instance ()
 Returns the shared AddonManager instance.

Protected Member Functions

 AddonManager ()
 ~AddonManager ()

Protected Attributes

std::vector< Addon * > addons
std::vector< std::string > ignored_addon_filenames


Detailed Description

Checks for, installs and removes Add-ons.

Definition at line 31 of file addon_manager.hpp.


Constructor & Destructor Documentation

AddonManager::AddonManager (  )  [protected]

Definition at line 71 of file addon_manager.cpp.

00071                            :
00072   addons(),
00073   ignored_addon_filenames()
00074 {
00075 #ifdef HAVE_LIBCURL
00076   curl_global_init(CURL_GLOBAL_ALL);
00077 #endif
00078 }

AddonManager::~AddonManager (  )  [protected]

Definition at line 80 of file addon_manager.cpp.

References addons.

00081 {
00082 #ifdef HAVE_LIBCURL
00083   curl_global_cleanup();
00084 #endif
00085 
00086   for (std::vector<Addon*>::iterator i = addons.begin(); i != addons.end(); i++) delete *i;
00087 }


Member Function Documentation

std::vector< Addon * > AddonManager::get_addons (  ) 

returns a list of installed Add-ons

Definition at line 90 of file addon_manager.cpp.

References addons.

Referenced by AddonMenu::refresh().

00091 {
00092   /*
00093     for (std::vector<Addon>::iterator it = installed_addons.begin(); it != installed_addons.end(); ++it) {
00094     Addon& addon = *it;
00095     if (addon.md5 == "") addon.md5 = calculate_md5(addon);
00096     }
00097   */
00098   return addons;
00099 }

void AddonManager::check_online (  ) 

downloads list of available Add-ons

Definition at line 102 of file addon_manager.cpp.

References addons, lisp::Lisp::get_lisp(), lisp::ListIterator::item(), lisp::ListIterator::lisp(), log_warning, lisp::ListIterator::next(), and lisp::Parser::parse().

Referenced by AddonMenu::check_menu().

00103 {
00104 #ifdef HAVE_LIBCURL
00105   char error_buffer[CURL_ERROR_SIZE+1];
00106 
00107   const char* baseUrl = "http://supertux.lethargik.org/addons/index.nfo";
00108   std::string addoninfos = "";
00109 
00110   CURL *curl_handle;
00111   curl_handle = curl_easy_init();
00112   curl_easy_setopt(curl_handle, CURLOPT_URL, baseUrl);
00113   curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
00114   curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_string_append);
00115   curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &addoninfos);
00116   curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
00117   curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
00118   curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
00119   curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
00120   curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
00121   CURLcode result = curl_easy_perform(curl_handle);
00122   curl_easy_cleanup(curl_handle);
00123 
00124   if (result != CURLE_OK) {
00125     std::string why = error_buffer[0] ? error_buffer : "unhandled error";
00126     throw std::runtime_error("Downloading Add-on list failed: " + why);
00127   }
00128 
00129   try {
00130     lisp::Parser parser;
00131     std::stringstream addoninfos_stream(addoninfos);
00132     const lisp::Lisp* root = parser.parse(addoninfos_stream, "supertux-addons");
00133 
00134     const lisp::Lisp* addons_lisp = root->get_lisp("supertux-addons");
00135     if(!addons_lisp) throw std::runtime_error("Downloaded file is not an Add-on list");
00136 
00137     lisp::ListIterator iter(addons_lisp);
00138     while(iter.next()) 
00139     {
00140       const std::string& token = iter.item();
00141       if(token != "supertux-addoninfo") 
00142       {
00143         log_warning << "Unknown token '" << token << "' in Add-on list" << std::endl;
00144         continue;
00145       }
00146       std::auto_ptr<Addon> addon(new Addon());
00147       addon->parse(*(iter.lisp()));
00148       addon->installed = false;
00149       addon->loaded = false;
00150 
00151       // make sure the list of known Add-ons does not already contain this one 
00152       bool exists = false;
00153       for (std::vector<Addon*>::const_iterator i = addons.begin(); i != addons.end(); i++) {
00154         if (**i == *addon) {
00155           exists = true; 
00156           break; 
00157         }
00158       }
00159 
00160       if (exists) 
00161       {
00162         // do nothing
00163       }
00164       else if (addon->suggested_filename.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) 
00165       {
00166         // make sure the Add-on's file name does not contain weird characters
00167         log_warning << "Add-on \"" << addon->title << "\" contains unsafe file name. Skipping." << std::endl;
00168       }
00169       else
00170       {
00171         addons.push_back(addon.release());
00172       }
00173     }
00174   } catch(std::exception& e) {
00175     std::stringstream msg;
00176     msg << "Problem when reading Add-on list: " << e.what();
00177     throw std::runtime_error(msg.str());
00178   }
00179 
00180 #endif
00181 }

void AddonManager::install ( Addon addon  ) 

Download and install Add-on.

Definition at line 184 of file addon_manager.cpp.

References enable(), Addon::get_md5(), Addon::http_url, Addon::installed, Addon::installed_absolute_filename, Addon::installed_physfs_filename, Addon::loaded, log_debug, Addon::stored_md5, and Addon::suggested_filename.

Referenced by AddonMenu::check_menu().

00185 {
00186 #ifdef HAVE_LIBCURL
00187 
00188   if (addon->installed) throw std::runtime_error("Tried installing installed Add-on");
00189 
00190   // make sure the Add-on's file name does not contain weird characters
00191   if (addon->suggested_filename.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
00192     throw std::runtime_error("Add-on has unsafe file name (\""+addon->suggested_filename+"\")");
00193   }
00194 
00195   std::string fileName = addon->suggested_filename;
00196 
00197   // make sure its file doesn't already exist
00198   if (PHYSFS_exists(fileName.c_str())) {
00199     fileName = addon->stored_md5 + "_" + addon->suggested_filename;
00200     if (PHYSFS_exists(fileName.c_str())) {
00201       throw std::runtime_error("Add-on of suggested filename already exists (\""+addon->suggested_filename+"\", \""+fileName+"\")");
00202     }
00203   }
00204 
00205   char error_buffer[CURL_ERROR_SIZE+1];
00206 
00207   char* url = (char*)malloc(addon->http_url.length() + 1);
00208   strncpy(url, addon->http_url.c_str(), addon->http_url.length() + 1);
00209 
00210   PHYSFS_file* f = PHYSFS_openWrite(fileName.c_str());
00211 
00212   log_debug << "Downloading \"" << url << "\"" << std::endl;
00213 
00214   CURL *curl_handle;
00215   curl_handle = curl_easy_init();
00216   curl_easy_setopt(curl_handle, CURLOPT_URL, url);
00217   curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "SuperTux/" PACKAGE_VERSION " libcURL");
00218   curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, my_curl_physfs_write);
00219   curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, f);
00220   curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, error_buffer);
00221   curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1);
00222   curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
00223   curl_easy_setopt(curl_handle, CURLOPT_FAILONERROR, 1);
00224   CURLcode result = curl_easy_perform(curl_handle);
00225   curl_easy_cleanup(curl_handle);
00226 
00227   PHYSFS_close(f);
00228 
00229   free(url);
00230 
00231   if (result != CURLE_OK) {
00232     PHYSFS_delete(fileName.c_str());
00233     std::string why = error_buffer[0] ? error_buffer : "unhandled error";
00234     throw std::runtime_error("Downloading Add-on failed: " + why);
00235   }
00236 
00237   addon->installed = true;
00238   addon->installed_physfs_filename = fileName;
00239   static const std::string writeDir = PHYSFS_getWriteDir();
00240   static const std::string dirSep = PHYSFS_getDirSeparator();
00241   addon->installed_absolute_filename = writeDir + dirSep + fileName;
00242   addon->loaded = false;
00243 
00244   if (addon->get_md5() != addon->stored_md5) {
00245     addon->installed = false;
00246     PHYSFS_delete(fileName.c_str());
00247     std::string why = "MD5 checksums differ"; 
00248     throw std::runtime_error("Downloading Add-on failed: " + why);
00249   }
00250 
00251   log_debug << "Finished downloading \"" << addon->installed_absolute_filename << "\". Enabling Add-on." << std::endl;
00252 
00253   enable(addon);
00254 
00255 #else
00256   (void) addon;
00257 #endif
00258 
00259 }

void AddonManager::remove ( Addon addon  ) 

Physically delete Add-on.

Definition at line 262 of file addon_manager.cpp.

References Addon::installed, Addon::installed_absolute_filename, Addon::installed_physfs_filename, log_debug, and unload().

00263 {
00264   if (!addon->installed) throw std::runtime_error("Tried removing non-installed Add-on");
00265 
00266   //FIXME: more checks
00267 
00268   // make sure the Add-on's file name does not contain weird characters
00269   if (addon->installed_physfs_filename.find_first_not_of("match.quiz-proxy_gwenblvdjfks0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") != std::string::npos) {
00270     throw std::runtime_error("Add-on has unsafe file name (\""+addon->installed_physfs_filename+"\")");
00271   }
00272 
00273   unload(addon);
00274 
00275   log_debug << "deleting file \"" << addon->installed_absolute_filename << "\"" << std::endl;
00276   PHYSFS_delete(addon->installed_absolute_filename.c_str());
00277   addon->installed = false;
00278 
00279   // FIXME: As we don't know anything more about it (e.g. where to get it), remove it from list of known Add-ons
00280 }

void AddonManager::disable ( Addon addon  ) 

Unload Add-on and mark as not to be loaded automatically.

Definition at line 283 of file addon_manager.cpp.

References ignored_addon_filenames, Addon::installed_physfs_filename, and unload().

Referenced by AddonMenu::check_menu().

00284 {
00285   unload(addon);
00286 
00287   std::string fileName = addon->installed_physfs_filename;
00288   if (std::find(ignored_addon_filenames.begin(), ignored_addon_filenames.end(), fileName) == ignored_addon_filenames.end()) {
00289     ignored_addon_filenames.push_back(fileName);
00290   }
00291 }

void AddonManager::enable ( Addon addon  ) 

Load Add-on and mark as to be loaded automatically.

Definition at line 294 of file addon_manager.cpp.

References ignored_addon_filenames, Addon::installed_physfs_filename, and load().

Referenced by AddonMenu::check_menu(), and install().

00295 {
00296   load(addon);
00297 
00298   std::string fileName = addon->installed_physfs_filename;
00299   std::vector<std::string>::iterator i = std::find(ignored_addon_filenames.begin(), ignored_addon_filenames.end(), fileName);
00300   if (i != ignored_addon_filenames.end()) {
00301     ignored_addon_filenames.erase(i);
00302   }
00303 }

void AddonManager::unload ( Addon addon  ) 

Remove Add-on from search path.

Definition at line 306 of file addon_manager.cpp.

References Addon::installed, Addon::installed_absolute_filename, Addon::loaded, log_debug, and log_warning.

Referenced by disable(), load_addons(), and remove().

00307 {
00308   if (!addon->installed) throw std::runtime_error("Tried unloading non-installed Add-on");
00309   if (!addon->loaded) return;
00310 
00311   log_debug << "Removing archive \"" << addon->installed_absolute_filename << "\" from search path" << std::endl;
00312   if (PHYSFS_removeFromSearchPath(addon->installed_absolute_filename.c_str()) == 0) {
00313     log_warning << "Could not remove " << addon->installed_absolute_filename << " from search path. Ignoring." << std::endl;
00314     return;
00315   }
00316 
00317   addon->loaded = false;
00318 }

void AddonManager::load ( Addon addon  ) 

Add Add-on to search path.

Definition at line 321 of file addon_manager.cpp.

References Addon::installed, Addon::installed_absolute_filename, Addon::loaded, log_debug, and log_warning.

Referenced by enable().

00322 {
00323   if (!addon->installed) throw std::runtime_error("Tried loading non-installed Add-on");
00324   if (addon->loaded) return;
00325 
00326   log_debug << "Adding archive \"" << addon->installed_absolute_filename << "\" to search path" << std::endl;
00327   if (PHYSFS_addToSearchPath(addon->installed_absolute_filename.c_str(), 0) == 0) {
00328     log_warning << "Could not add " << addon->installed_absolute_filename << " to search path. Ignoring." << std::endl;
00329     return;
00330   }
00331 
00332   addon->loaded = true;
00333 }

void AddonManager::load_addons (  ) 

Loads all enabled Add-ons, i.e.

adds them to the search path

Definition at line 336 of file addon_manager.cpp.

References addons, ignored_addon_filenames, Addon::installed, Addon::installed_absolute_filename, Addon::installed_physfs_filename, Addon::loaded, log_debug, log_warning, Addon::parse(), and unload().

Referenced by Main::run().

00337 {
00338   // unload all Addons and forget about them
00339   for (std::vector<Addon*>::iterator i = addons.begin(); i != addons.end(); i++) {
00340     if ((*i)->installed && (*i)->loaded) unload(*i);
00341     delete *i;
00342   }
00343   addons.clear();
00344 
00345   // Search for archives and add them to the search path
00346   char** rc = PHYSFS_enumerateFiles("/");
00347 
00348   for(char** i = rc; *i != 0; ++i) {
00349 
00350     // get filename of potential archive
00351     std::string fileName = *i;
00352 
00353     const std::string archiveDir = PHYSFS_getRealDir(fileName.c_str());
00354     static const std::string dirSep = PHYSFS_getDirSeparator();
00355     std::string fullFilename = archiveDir + dirSep + fileName;
00356 
00357     /*
00358     // make sure it's in the writeDir
00359     static const std::string writeDir = PHYSFS_getWriteDir();
00360     if (fileName.compare(0, writeDir.length(), writeDir) != 0) continue;
00361     */
00362 
00363     // make sure it looks like an archive
00364     static const std::string archiveExt = ".zip";
00365     if (fullFilename.compare(fullFilename.length()-archiveExt.length(), archiveExt.length(), archiveExt) != 0) continue;
00366 
00367     // make sure it exists
00368     struct stat stats;
00369     if (stat(fullFilename.c_str(), &stats) != 0) continue;
00370 
00371     // make sure it's an actual file
00372     if (!S_ISREG(stats.st_mode)) continue;
00373 
00374     log_debug << "Found archive \"" << fullFilename << "\"" << std::endl;
00375 
00376     // add archive to search path
00377     PHYSFS_addToSearchPath(fullFilename.c_str(), 0);
00378 
00379     // Search for infoFiles
00380     std::string infoFileName = "";
00381     char** rc2 = PHYSFS_enumerateFiles("/");
00382     for(char** i = rc2; *i != 0; ++i) {
00383 
00384       // get filename of potential infoFile
00385       std::string potentialInfoFileName = *i;
00386 
00387       // make sure it looks like an infoFile
00388       static const std::string infoExt = ".nfo";
00389       if (potentialInfoFileName.length() <= infoExt.length())
00390         continue;
00391 
00392       if (potentialInfoFileName.compare(potentialInfoFileName.length()-infoExt.length(), infoExt.length(), infoExt) != 0)
00393         continue;
00394 
00395       // make sure it's in the current archive
00396       std::string infoFileDir = PHYSFS_getRealDir(potentialInfoFileName.c_str());
00397       if (infoFileDir != fullFilename) continue;
00398 
00399       // found infoFileName
00400       infoFileName = potentialInfoFileName;
00401       break;
00402     }
00403     PHYSFS_freeList(rc2);
00404 
00405     // if we have an infoFile, it's an Addon
00406     if (infoFileName != "") {
00407       try {
00408         Addon* addon = new Addon();
00409         addon->parse(infoFileName);
00410         addon->installed = true;
00411         addon->installed_physfs_filename = fileName;
00412         addon->installed_absolute_filename = fullFilename;
00413         addon->loaded = true;
00414         addons.push_back(addon);
00415 
00416         // check if the Addon is disabled 
00417         if (std::find(ignored_addon_filenames.begin(), ignored_addon_filenames.end(), fileName) != ignored_addon_filenames.end()) {
00418           unload(addon);
00419         }
00420 
00421       } catch (const std::runtime_error& e) {
00422         log_warning << "Could not load add-on info for " << fullFilename << ", loading as unmanaged:" << e.what() << std::endl;
00423       }
00424     }
00425 
00426   }
00427 
00428   PHYSFS_freeList(rc);
00429 }

AddonManager & AddonManager::get_instance (  )  [static]

Returns the shared AddonManager instance.

Definition at line 65 of file addon_manager.cpp.

Referenced by AddonMenu::check_menu(), Config::load(), AddonMenu::refresh(), Main::run(), and Config::save().

00066 {
00067   static AddonManager instance;
00068   return instance;
00069 }

void AddonManager::write ( Writer writer  ) 

Write AddonManager configuration to Lisp.

Definition at line 438 of file addon_manager.cpp.

References ignored_addon_filenames, and lisp::Writer::write().

Referenced by Config::save().

00439 {
00440   writer.write("disabled-addons", ignored_addon_filenames); 
00441 }

void AddonManager::read ( const Reader lisp  ) 

Read AddonManager configuration from Lisp.

Definition at line 432 of file addon_manager.cpp.

References lisp::Lisp::get(), and ignored_addon_filenames.

Referenced by Config::load().

00433 {
00434   lisp.get("disabled-addons", ignored_addon_filenames); 
00435 }


Member Data Documentation

std::vector<Addon*> AddonManager::addons [protected]

Definition at line 95 of file addon_manager.hpp.

Referenced by check_online(), get_addons(), load_addons(), and ~AddonManager().

std::vector<std::string> AddonManager::ignored_addon_filenames [protected]

Definition at line 96 of file addon_manager.hpp.

Referenced by disable(), enable(), load_addons(), read(), and write().


The documentation for this class was generated from the following files:
Generated on Mon Apr 14 03:38:31 2014 for SuperTux by  doxygen 1.5.1