/*MT* MediaTomb - http://www.mediatomb.cc/ config_manager.cc - this file is part of MediaTomb. Copyright (C) 2005 Gena Batyan , Sergey 'Jin' Bostandzhyan Copyright (C) 2006-2007 Gena Batyan , Sergey 'Jin' Bostandzhyan , Leonhard Wimmer MediaTomb is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. MediaTomb 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 version 2 along with MediaTomb; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. $Id: config_manager.cc 1195 2007-03-03 19:06:56Z jin_eld $ */ /// \file config_manager.cc #ifdef HAVE_CONFIG_H #include "autoconfig.h" #endif #include #include "uuid/uuid.h" #include "common.h" #include "config_manager.h" #include "storage.h" #include #include #include "tools.h" #include "string_converter.h" #if defined(HAVE_LANGINFO_H) && defined(HAVE_LOCALE_H) #include #include #endif using namespace zmm; using namespace mxml; SINGLETON_MUTEX(ConfigManager, false); String ConfigManager::filename = nil; String ConfigManager::userhome = nil; ConfigManager::~ConfigManager() { filename = nil; userhome = nil; } void ConfigManager::setStaticArgs(String _filename, String _userhome) { filename = _filename; userhome = _userhome; } ConfigManager::ConfigManager() : Singleton() { String home = userhome + DIR_SEPARATOR + DEFAULT_CONFIG_HOME; bool home_ok = true; if (filename == nil) { // we are looking for ~/.mediatomb if (home_ok && (!check_path(userhome + DIR_SEPARATOR + DEFAULT_CONFIG_HOME + DIR_SEPARATOR + DEFAULT_CONFIG_NAME))) { home_ok = false; } else { filename = home + DIR_SEPARATOR + DEFAULT_CONFIG_NAME; } if ((!home_ok) && (string_ok(userhome))) { userhome = normalizePath(userhome); filename = createDefaultConfig(userhome); } } if (filename == nil) { throw _Exception(_("\nThe server configuration file could not be found in ~/.mediatomb\n") + "MediaTomb could not determine your home directory - automatic setup failed.\n" + "Try specifying an alternative configuration file on the command line.\n" + "For a list of options run: mediatomb -h\n"); } log_info("Loading configuration from: %s\n", filename.c_str()); load(filename); prepare_udn(); validate(home); } String ConfigManager::construct_path(String path) { String home = getOption(_("/server/home")); if (path.charAt(0) == '/') return path; #if defined(__CYGWIN__) if ((path.length() > 1) && (path.charAt(1) == ':')) return path; #endif if (home == "." && path.charAt(0) == '.') return path; if (home == "") return _(".") + DIR_SEPARATOR + path; else return home + DIR_SEPARATOR + path; } String ConfigManager::createDefaultConfig(String userhome) { bool mysql_flag = false; String homepath = userhome + DIR_SEPARATOR + DEFAULT_CONFIG_HOME; if (!check_path(homepath, true)) { if (mkdir(homepath.c_str(), 0777) < 0) { throw _Exception(_("Could not create directory ") + homepath + " : " + strerror(errno)); } } String config_filename = homepath + DIR_SEPARATOR + DEFAULT_CONFIG_NAME; Ref config(new Element(_("config"))); config->addAttribute(_("xmlns"), _(XML_XMLNS)); config->addAttribute(_("xmlns:xsi"), _(XML_XMLNS_XSI)); config->addAttribute(_("xsi:schemaLocation"), _(XML_XSI_SCHEMA_LOCATION)); Ref server(new Element(_("server"))); Ref ui(new Element(_("ui"))); ui->addAttribute(_("enabled"), _(DEFAULT_UI_EN_VALUE)); Refaccounts(new Element(_("accounts"))); accounts->addAttribute(_("enabled"), _(DEFAULT_ACCOUNTS_EN_VALUE)); accounts->addAttribute(_("session-timeout"), String::from(DEFAULT_SESSION_TIMEOUT)); ui->appendChild(accounts); server->appendChild(ui); server->appendTextChild(_("name"), _(PACKAGE_NAME)); Ref udn(new Element(_("udn"))); server->appendChild(udn); server->appendTextChild(_("home"), homepath); server->appendTextChild(_("webroot"), String(_(PACKAGE_DATADIR)) + DIR_SEPARATOR + _(DEFAULT_WEB_DIR)); Ref storage(new Element(_("storage"))); storage->addAttribute(_("driver"), _(DEFAULT_STORAGE_DRIVER)); #ifdef HAVE_SQLITE3 storage->appendTextChild(_("database-file"), _(DEFAULT_SQLITE3_DB_FILENAME)); #else storage->appendTextChild(_("host"), _(DEFAULT_MYSQL_HOST)); storage->appendTextChild(_("username"), _(DEFAULT_MYSQL_USER)); // storage->appendTextChild(_("password"), _(DEFAULT_MYSQL_PASSWORD)); storage->appendTextChild(_("database"), _(DEFAULT_MYSQL_DB)); mysql_flag = true; #endif server->appendChild(storage); config->appendChild(server); Ref import(new Element(_("import"))); import->addAttribute(_("hidden-files"), _(DEFAULT_HIDDEN_FILES_VALUE)); Ref layout(new Element(_("virtual-layout"))); layout->addAttribute(_("type"), _(DEFAULT_LAYOUT_TYPE)); #ifdef HAVE_JS layout->appendTextChild(_("script"), String(_(PACKAGE_DATADIR)) + DIR_SEPARATOR + _(DEFAULT_JS_DIR) + DIR_SEPARATOR + _(DEFAULT_IMPORT_SCRIPT)); #endif import->appendChild(layout); String map_file = _(PACKAGE_DATADIR) + DIR_SEPARATOR + CONFIG_MAPPINGS_TEMPLATE; try { Ref parser(new Parser()); Ref mappings(new Element(_("mappings"))); mappings = parser->parseFile(map_file); import->appendChild(mappings); } catch (ParseException pe) { log_error("Error parsing template file: %s line %d:\n%s\n", pe.context->location.c_str(), pe.context->line, pe.getMessage().c_str()); exit(EXIT_FAILURE); } catch (Exception ex) { log_error("Could not import mapping template file from %s\n", map_file.c_str()); } config->appendChild(import); save_text(config_filename, config->print()); log_info("MediaTomb configuration was created in: %s\n", config_filename.c_str()); if (mysql_flag) { throw _Exception(_("You are using MySQL! Please edit ") + config_filename + " and enter your MySQL host/username/password!"); } return config_filename; } void ConfigManager::validate(String serverhome) { String temp; log_info("Checking configuration...\n"); // first check if the config file itself looks ok, it must have a config // and a server tag if (root->getName() != "config") throw _Exception(_("Error in config file: tag not found")); if (root->getChild(_("server")) == nil) throw _Exception(_("Error in config file: tag not found")); // now go through the mandatory parameters, if something is missing // here we will not start the server // temp = checkOption_("/server/home"); // check_path_ex(temp, true); getOption(_("/server/home"), serverhome); prepare_path(_("/server/home"), true); // temp = checkOption_("/server/webroot"); // check_path_ex(construct_path(temp), true); prepare_path(_("/server/webroot"), true); if (string_ok(getOption(_("/server/servedir"), _("")))) prepare_path(_("/server/servedir"), true); // udn should be already prepared checkOptionString(_("/server/udn")); checkOptionString(_("/server/storage/attribute::driver")); String dbDriver = getOption(_("/server/storage/attribute::driver")); // checking database driver options do { #ifdef HAVE_SQLITE3 if (dbDriver == "sqlite3") { prepare_path(_("/server/storage/database-file"), false, true); break; } #endif #ifdef HAVE_MYSQL if (dbDriver == "mysql") { getOption(_("/server/storage/host"), _(DEFAULT_MYSQL_HOST)); getOption(_("/server/storage/database"), _(DEFAULT_MYSQL_DB)); getOption(_("/server/storage/username"), _(DEFAULT_MYSQL_USER)); getOption(_("/server/storage/port"), _("0")); //getOption(_("/server/storage/socket")); - checked in mysql_storage.cc //getOption(_("/server/storage/password")); - checked in mysql_storage.cc break; } #endif // other database types... throw _Exception(_("Unknown storage driver: ") + dbDriver); } while (false); // temp = checkOption_("/server/storage/database-file"); // check_path_ex(construct_path(temp)); // now go through the optional settings and fix them if anything is missing temp = getOption(_("/server/ui/attribute::enabled"), _(DEFAULT_UI_EN_VALUE)); if ((temp != "yes") && (temp != "no")) throw _Exception(_("Error in config file: incorrect parameter for attribute")); temp = getOption(_("/server/ui/attribute::poll-when-idle"), _(DEFAULT_POLL_WHEN_IDLE_VALUE)); if ((temp != "yes") && (temp != "no")) throw _Exception(_("Error in config file: incorrect parameter for attribute")); int i = getIntOption(_("/server/ui/attribute::poll-interval"), DEFAULT_POLL_INTERVAL); if (i < 1) throw _Exception(_("Error in config file: incorrect parameter for attribute")); int ipp_default = getIntOption(_("/server/ui/items-per-page/attribute::default"), DEFAULT_ITEMS_PER_PAGE_2); if (i < 1) throw _Exception(_("Error in config file: incorrect parameter for attribute")); // now get the option list for the drop down menu Ref element = getElement(_("/server/ui/items-per-page")); // create default structure if (element->childCount() == 0) { if ((ipp_default != DEFAULT_ITEMS_PER_PAGE_1) && (ipp_default != DEFAULT_ITEMS_PER_PAGE_2) && (ipp_default != DEFAULT_ITEMS_PER_PAGE_3) && (ipp_default != DEFAULT_ITEMS_PER_PAGE_4)) { throw _Exception(_("Error in config file: you specified an value that is not listed in the options")); } element->appendTextChild(_("option"), String::from(DEFAULT_ITEMS_PER_PAGE_1)); element->appendTextChild(_("option"), String::from(DEFAULT_ITEMS_PER_PAGE_2)); element->appendTextChild(_("option"), String::from(DEFAULT_ITEMS_PER_PAGE_3)); element->appendTextChild(_("option"), String::from(DEFAULT_ITEMS_PER_PAGE_4)); } else // validate user settings { bool default_found = false; for (int j = 0; j < element->childCount(); j++) { Ref child = element->getChild(j); if (child->getName() == "option") { i = child->getText().toInt(); if (i < 1) throw _Exception(_("Error in config file: incorrect