/*
This file is part of Subsonic.
Subsonic 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 3 of the License, or
(at your option) any later version.
Subsonic 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 Subsonic. If not, see .
Copyright 2009 (C) Sindre Mehus
*/
package net.sourceforge.subsonic.service;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Properties;
import java.util.StringTokenizer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import net.sourceforge.subsonic.Logger;
import net.sourceforge.subsonic.dao.AvatarDao;
import net.sourceforge.subsonic.dao.InternetRadioDao;
import net.sourceforge.subsonic.dao.MusicFolderDao;
import net.sourceforge.subsonic.dao.UserDao;
import net.sourceforge.subsonic.domain.Avatar;
import net.sourceforge.subsonic.domain.InternetRadio;
import net.sourceforge.subsonic.domain.MusicFolder;
import net.sourceforge.subsonic.domain.Theme;
import net.sourceforge.subsonic.domain.UserSettings;
import net.sourceforge.subsonic.util.FileUtil;
import net.sourceforge.subsonic.util.StringUtil;
import net.sourceforge.subsonic.util.Util;
/**
* Provides persistent storage of application settings and preferences.
*
* @author Sindre Mehus
*/
public class SettingsService {
// Subsonic home directory.
private static final File SUBSONIC_HOME_WINDOWS = new File("c:/subsonic");
private static final File SUBSONIC_HOME_OTHER = new File("/var/subsonic");
// Global settings.
private static final String KEY_INDEX_STRING = "IndexString";
private static final String KEY_IGNORED_ARTICLES = "IgnoredArticles";
private static final String KEY_SHORTCUTS = "Shortcuts";
private static final String KEY_PLAYLIST_FOLDER = "PlaylistFolder";
private static final String KEY_MUSIC_FILE_TYPES = "MusicFileTypes";
private static final String KEY_VIDEO_FILE_TYPES = "VideoFileTypes";
private static final String KEY_COVER_ART_FILE_TYPES = "CoverArtFileTypes";
private static final String KEY_COVER_ART_LIMIT = "CoverArtLimit";
private static final String KEY_WELCOME_TITLE = "WelcomeTitle";
private static final String KEY_WELCOME_SUBTITLE = "WelcomeSubtitle";
private static final String KEY_WELCOME_MESSAGE = "WelcomeMessage2";
private static final String KEY_LOGIN_MESSAGE = "LoginMessage";
private static final String KEY_LOCALE_LANGUAGE = "LocaleLanguage";
private static final String KEY_LOCALE_COUNTRY = "LocaleCountry";
private static final String KEY_LOCALE_VARIANT = "LocaleVariant";
private static final String KEY_THEME_ID = "Theme";
private static final String KEY_INDEX_CREATION_INTERVAL = "IndexCreationInterval";
private static final String KEY_INDEX_CREATION_HOUR = "IndexCreationHour";
private static final String KEY_FAST_CACHE_ENABLED = "FastCacheEnabled";
private static final String KEY_PODCAST_UPDATE_INTERVAL = "PodcastUpdateInterval";
private static final String KEY_PODCAST_FOLDER = "PodcastFolder";
private static final String KEY_PODCAST_EPISODE_RETENTION_COUNT = "PodcastEpisodeRetentionCount";
private static final String KEY_PODCAST_EPISODE_DOWNLOAD_COUNT = "PodcastEpisodeDownloadCount";
private static final String KEY_DOWNLOAD_BITRATE_LIMIT = "DownloadBitrateLimit";
private static final String KEY_UPLOAD_BITRATE_LIMIT = "UploadBitrateLimit";
private static final String KEY_STREAM_PORT = "StreamPort";
private static final String KEY_LICENSE_EMAIL = "LicenseEmail";
private static final String KEY_LICENSE_CODE = "LicenseCode";
private static final String KEY_LICENSE_DATE = "LicenseDate";
private static final String KEY_DOWNSAMPLING_COMMAND = "DownsamplingCommand3";
private static final String KEY_JUKEBOX_COMMAND = "JukeboxCommand";
private static final String KEY_REWRITE_URL = "RewriteUrl";
private static final String KEY_LDAP_ENABLED = "LdapEnabled";
private static final String KEY_LDAP_URL = "LdapUrl";
private static final String KEY_LDAP_MANAGER_DN = "LdapManagerDn";
private static final String KEY_LDAP_MANAGER_PASSWORD = "LdapManagerPassword";
private static final String KEY_LDAP_SEARCH_FILTER = "LdapSearchFilter";
private static final String KEY_LDAP_AUTO_SHADOWING = "LdapAutoShadowing";
private static final String KEY_GETTING_STARTED_ENABLED = "GettingStartedEnabled";
private static final String KEY_PORT_FORWARDING_ENABLED = "PortForwardingEnabled";
private static final String KEY_PORT = "Port";
private static final String KEY_HTTPS_PORT = "HttpsPort";
private static final String KEY_URL_REDIRECTION_ENABLED = "UrlRedirectionEnabled";
private static final String KEY_URL_REDIRECT_FROM = "UrlRedirectFrom";
private static final String KEY_URL_REDIRECT_TRIAL_EXPIRES = "UrlRedirectTrialExpires";
private static final String KEY_URL_REDIRECT_CONTEXT_PATH = "UrlRedirectContextPath";
private static final String KEY_REST_TRIAL_EXPIRES = "RestTrialExpires-";
private static final String KEY_VIDEO_TRIAL_EXPIRES = "VideoTrialExpires";
private static final String KEY_SERVER_ID = "ServerId";
private static final String KEY_SETTINGS_CHANGED = "SettingsChanged";
private static final String KEY_LAST_SCANNED = "LastScanned";
private static final String KEY_ORGANIZE_BY_FOLDER_STRUCTURE = "OrganizeByFolderStructure";
private static final String KEY_SORT_ALBUMS_BY_YEAR = "SortAlbumsByYear";
// Default values.
private static final String DEFAULT_INDEX_STRING = "A B C D E F G H I J K L M N O P Q R S T U V W X-Z(XYZ)";
private static final String DEFAULT_IGNORED_ARTICLES = "The El La Los Las Le Les";
private static final String DEFAULT_SHORTCUTS = "New Incoming Podcast";
private static final String DEFAULT_PLAYLIST_FOLDER = Util.getDefaultPlaylistFolder();
private static final String DEFAULT_MUSIC_FILE_TYPES = "mp3 ogg oga aac m4a flac wav wma aif aiff ape mpc shn";
private static final String DEFAULT_VIDEO_FILE_TYPES = "flv avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts";
private static final String DEFAULT_COVER_ART_FILE_TYPES = "cover.jpg folder.jpg jpg jpeg gif png";
private static final int DEFAULT_COVER_ART_LIMIT = 30;
private static final String DEFAULT_WELCOME_TITLE = "Welcome to Subsonic!";
private static final String DEFAULT_WELCOME_SUBTITLE = null;
private static final String DEFAULT_WELCOME_MESSAGE = "__Welcome to Subsonic!__\n" +
"\\\\ \\\\\n" +
"Subsonic is a free, web-based media streamer, providing ubiquitous access to your music. \n" +
"\\\\ \\\\\n" +
"Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple " +
"players simultaneously, for instance to one player in your kitchen and another in your living room.\n" +
"\\\\ \\\\\n" +
"To change or remove this message, log in with administrator rights and go to {link:Settings > General|generalSettings.view}.";
private static final String DEFAULT_LOGIN_MESSAGE = null;
private static final String DEFAULT_LOCALE_LANGUAGE = "en";
private static final String DEFAULT_LOCALE_COUNTRY = "";
private static final String DEFAULT_LOCALE_VARIANT = "";
private static final String DEFAULT_THEME_ID = "default";
private static final int DEFAULT_INDEX_CREATION_INTERVAL = 1;
private static final int DEFAULT_INDEX_CREATION_HOUR = 3;
private static final boolean DEFAULT_FAST_CACHE_ENABLED = false;
private static final int DEFAULT_PODCAST_UPDATE_INTERVAL = 24;
private static final String DEFAULT_PODCAST_FOLDER = Util.getDefaultPodcastFolder();
private static final int DEFAULT_PODCAST_EPISODE_RETENTION_COUNT = 10;
private static final int DEFAULT_PODCAST_EPISODE_DOWNLOAD_COUNT = 1;
private static final long DEFAULT_DOWNLOAD_BITRATE_LIMIT = 0;
private static final long DEFAULT_UPLOAD_BITRATE_LIMIT = 0;
private static final long DEFAULT_STREAM_PORT = 0;
private static final String DEFAULT_LICENSE_EMAIL = null;
private static final String DEFAULT_LICENSE_CODE = null;
private static final String DEFAULT_LICENSE_DATE = null;
private static final String DEFAULT_DOWNSAMPLING_COMMAND = "ffmpeg -i %s -ab %bk -v 0 -f mp3 -";
private static final String DEFAULT_JUKEBOX_COMMAND = "ffmpeg -ss %o -i %s -v 0 -f au -";
private static final boolean DEFAULT_REWRITE_URL = true;
private static final boolean DEFAULT_LDAP_ENABLED = false;
private static final String DEFAULT_LDAP_URL = "ldap://host.domain.com:389/cn=Users,dc=domain,dc=com";
private static final String DEFAULT_LDAP_MANAGER_DN = null;
private static final String DEFAULT_LDAP_MANAGER_PASSWORD = null;
private static final String DEFAULT_LDAP_SEARCH_FILTER = "(sAMAccountName={0})";
private static final boolean DEFAULT_LDAP_AUTO_SHADOWING = false;
private static final boolean DEFAULT_PORT_FORWARDING_ENABLED = false;
private static final boolean DEFAULT_GETTING_STARTED_ENABLED = true;
private static final int DEFAULT_PORT = 80;
private static final int DEFAULT_HTTPS_PORT = 0;
private static final boolean DEFAULT_URL_REDIRECTION_ENABLED = false;
private static final String DEFAULT_URL_REDIRECT_FROM = "yourname";
private static final String DEFAULT_URL_REDIRECT_TRIAL_EXPIRES = null;
private static final String DEFAULT_URL_REDIRECT_CONTEXT_PATH = null;
private static final String DEFAULT_REST_TRIAL_EXPIRES = null;
private static final String DEFAULT_VIDEO_TRIAL_EXPIRES = null;
private static final String DEFAULT_SERVER_ID = null;
private static final long DEFAULT_SETTINGS_CHANGED = 0L;
private static final boolean DEFAULT_ORGANIZE_BY_FOLDER_STRUCTURE = true;
private static final boolean DEFAULT_SORT_ALBUMS_BY_YEAR = true;
// Array of obsolete keys. Used to clean property file.
private static final List OBSOLETE_KEYS = Arrays.asList("PortForwardingPublicPort", "PortForwardingLocalPort",
"DownsamplingCommand", "DownsamplingCommand2", "AutoCoverBatch", "MusicMask", "VideoMask", "CoverArtMask");
private static final String LOCALES_FILE = "/net/sourceforge/subsonic/i18n/locales.txt";
private static final String THEMES_FILE = "/net/sourceforge/subsonic/theme/themes.txt";
private static final Logger LOG = Logger.getLogger(SettingsService.class);
private Properties properties = new Properties();
private List themes;
private List locales;
private InternetRadioDao internetRadioDao;
private MusicFolderDao musicFolderDao;
private UserDao userDao;
private AvatarDao avatarDao;
private VersionService versionService;
private String[] cachedCoverArtFileTypesArray;
private String[] cachedMusicFileTypesArray;
private String[] cachedVideoFileTypesArray;
private List cachedMusicFolders;
private static File subsonicHome;
private boolean licenseValidated = true;
public SettingsService() {
File propertyFile = getPropertyFile();
if (propertyFile.exists()) {
FileInputStream in = null;
try {
in = new FileInputStream(propertyFile);
properties.load(in);
} catch (Exception x) {
LOG.error("Unable to read from property file.", x);
} finally {
IOUtils.closeQuietly(in);
}
// Remove obsolete properties.
for (Iterator