Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Untitled

By: a guest on Jan 9th, 2014  |  syntax: Java  |  size: 47.29 KB  |  views: 135  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. /*
  2.  This file is part of Subsonic.
  3.  
  4.  Subsonic 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.  Subsonic 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 Subsonic.  If not, see <http://www.gnu.org/licenses/>.
  16.  
  17.  Copyright 2009 (C) Sindre Mehus
  18.  */
  19. package net.sourceforge.subsonic.service;
  20.  
  21. import java.io.File;
  22. import java.io.FileInputStream;
  23. import java.io.FileOutputStream;
  24. import java.io.IOException;
  25. import java.io.InputStream;
  26. import java.io.OutputStream;
  27. import java.util.ArrayList;
  28. import java.util.Arrays;
  29. import java.util.Date;
  30. import java.util.Iterator;
  31. import java.util.List;
  32. import java.util.Locale;
  33. import java.util.Properties;
  34. import java.util.StringTokenizer;
  35. import java.util.concurrent.Executors;
  36. import java.util.concurrent.ScheduledExecutorService;
  37. import java.util.concurrent.ScheduledFuture;
  38. import java.util.concurrent.TimeUnit;
  39.  
  40. import org.apache.commons.io.IOUtils;
  41. import org.apache.commons.lang.StringUtils;
  42. import org.apache.http.client.HttpClient;
  43. import org.apache.http.client.ResponseHandler;
  44. import org.apache.http.client.methods.HttpGet;
  45. import org.apache.http.impl.client.BasicResponseHandler;
  46. import org.apache.http.impl.client.DefaultHttpClient;
  47. import org.apache.http.params.HttpConnectionParams;
  48.  
  49. import net.sourceforge.subsonic.Logger;
  50. import net.sourceforge.subsonic.dao.AvatarDao;
  51. import net.sourceforge.subsonic.dao.InternetRadioDao;
  52. import net.sourceforge.subsonic.dao.MusicFolderDao;
  53. import net.sourceforge.subsonic.dao.UserDao;
  54. import net.sourceforge.subsonic.domain.Avatar;
  55. import net.sourceforge.subsonic.domain.InternetRadio;
  56. import net.sourceforge.subsonic.domain.LicenseInfo;
  57. import net.sourceforge.subsonic.domain.MediaLibraryStatistics;
  58. import net.sourceforge.subsonic.domain.MusicFolder;
  59. import net.sourceforge.subsonic.domain.Theme;
  60. import net.sourceforge.subsonic.domain.UserSettings;
  61. import net.sourceforge.subsonic.util.FileUtil;
  62. import net.sourceforge.subsonic.util.StringUtil;
  63. import net.sourceforge.subsonic.util.Util;
  64.  
  65. /**
  66.  * Provides persistent storage of application settings and preferences.
  67.  *
  68.  * @author Sindre Mehus
  69.  */
  70. public class SettingsService {
  71.  
  72.     // Subsonic home directory.
  73.     private static final File SUBSONIC_HOME_WINDOWS = new File("c:/subsonic");
  74.     private static final File SUBSONIC_HOME_OTHER = new File("/var/subsonic");
  75.  
  76.     // Number of free trial days.
  77.     public static final long TRIAL_DAYS = 30L;
  78.  
  79.     // Global settings.
  80.     private static final String KEY_INDEX_STRING = "IndexString";
  81.     private static final String KEY_IGNORED_ARTICLES = "IgnoredArticles";
  82.     private static final String KEY_SHORTCUTS = "Shortcuts";
  83.     private static final String KEY_PLAYLIST_FOLDER = "PlaylistFolder";
  84.     private static final String KEY_MUSIC_FILE_TYPES = "MusicFileTypes";
  85.     private static final String KEY_VIDEO_FILE_TYPES = "VideoFileTypes";
  86.     private static final String KEY_COVER_ART_FILE_TYPES = "CoverArtFileTypes";
  87.     private static final String KEY_COVER_ART_LIMIT = "CoverArtLimit";
  88.     private static final String KEY_WELCOME_TITLE = "WelcomeTitle";
  89.     private static final String KEY_WELCOME_SUBTITLE = "WelcomeSubtitle";
  90.     private static final String KEY_WELCOME_MESSAGE = "WelcomeMessage2";
  91.     private static final String KEY_LOGIN_MESSAGE = "LoginMessage";
  92.     private static final String KEY_LOCALE_LANGUAGE = "LocaleLanguage";
  93.     private static final String KEY_LOCALE_COUNTRY = "LocaleCountry";
  94.     private static final String KEY_LOCALE_VARIANT = "LocaleVariant";
  95.     private static final String KEY_THEME_ID = "Theme";
  96.     private static final String KEY_INDEX_CREATION_INTERVAL = "IndexCreationInterval";
  97.     private static final String KEY_INDEX_CREATION_HOUR = "IndexCreationHour";
  98.     private static final String KEY_FAST_CACHE_ENABLED = "FastCacheEnabled";
  99.     private static final String KEY_PODCAST_UPDATE_INTERVAL = "PodcastUpdateInterval";
  100.     private static final String KEY_PODCAST_FOLDER = "PodcastFolder";
  101.     private static final String KEY_PODCAST_EPISODE_RETENTION_COUNT = "PodcastEpisodeRetentionCount";
  102.     private static final String KEY_PODCAST_EPISODE_DOWNLOAD_COUNT = "PodcastEpisodeDownloadCount";
  103.     private static final String KEY_DOWNLOAD_BITRATE_LIMIT = "DownloadBitrateLimit";
  104.     private static final String KEY_UPLOAD_BITRATE_LIMIT = "UploadBitrateLimit";
  105.     private static final String KEY_STREAM_PORT = "StreamPort";
  106.     private static final String KEY_LICENSE_EMAIL = "LicenseEmail";
  107.     private static final String KEY_LICENSE_CODE = "LicenseCode";
  108.     private static final String KEY_LICENSE_DATE = "LicenseDate";
  109.     private static final String KEY_DOWNSAMPLING_COMMAND = "DownsamplingCommand3";
  110.     private static final String KEY_HLS_COMMAND = "HlsCommand2";
  111.     private static final String KEY_JUKEBOX_COMMAND = "JukeboxCommand";
  112.     private static final String KEY_REWRITE_URL = "RewriteUrl";
  113.     private static final String KEY_LDAP_ENABLED = "LdapEnabled";
  114.     private static final String KEY_LDAP_URL = "LdapUrl";
  115.     private static final String KEY_LDAP_MANAGER_DN = "LdapManagerDn";
  116.     private static final String KEY_LDAP_MANAGER_PASSWORD = "LdapManagerPassword";
  117.     private static final String KEY_LDAP_SEARCH_FILTER = "LdapSearchFilter";
  118.     private static final String KEY_LDAP_AUTO_SHADOWING = "LdapAutoShadowing";
  119.     private static final String KEY_GETTING_STARTED_ENABLED = "GettingStartedEnabled";
  120.     private static final String KEY_PORT_FORWARDING_ENABLED = "PortForwardingEnabled";
  121.     private static final String KEY_PORT = "Port";
  122.     private static final String KEY_HTTPS_PORT = "HttpsPort";
  123.     private static final String KEY_URL_REDIRECTION_ENABLED = "UrlRedirectionEnabled";
  124.     private static final String KEY_URL_REDIRECT_FROM = "UrlRedirectFrom";
  125.     private static final String KEY_URL_REDIRECT_CONTEXT_PATH = "UrlRedirectContextPath";
  126.     private static final String KEY_SERVER_ID = "ServerId";
  127.     private static final String KEY_SETTINGS_CHANGED = "SettingsChanged";
  128.     private static final String KEY_LAST_SCANNED = "LastScanned";
  129.     private static final String KEY_ORGANIZE_BY_FOLDER_STRUCTURE = "OrganizeByFolderStructure";
  130.     private static final String KEY_SORT_ALBUMS_BY_YEAR = "SortAlbumsByYear";
  131.     private static final String KEY_MEDIA_LIBRARY_STATISTICS = "MediaLibraryStatistics";
  132.     private static final String KEY_TRIAL_EXPIRES = "TrialExpires";
  133.     private static final String KEY_DLNA_ENABLED = "DlnaEnabled";
  134.  
  135.     // Default values.
  136.     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)";
  137.     private static final String DEFAULT_IGNORED_ARTICLES = "The El La Los Las Le Les";
  138.     private static final String DEFAULT_SHORTCUTS = "New Incoming Podcast";
  139.     private static final String DEFAULT_PLAYLIST_FOLDER = Util.getDefaultPlaylistFolder();
  140.     private static final String DEFAULT_MUSIC_FILE_TYPES = "mp3 ogg oga aac m4a flac wav wma aif aiff ape mpc shn";
  141.     private static final String DEFAULT_VIDEO_FILE_TYPES = "flv avi mpg mpeg mp4 m4v mkv mov wmv ogv divx m2ts";
  142.     private static final String DEFAULT_COVER_ART_FILE_TYPES = "cover.jpg folder.jpg jpg jpeg gif png";
  143.     private static final int DEFAULT_COVER_ART_LIMIT = 50;
  144.     private static final String DEFAULT_WELCOME_TITLE = "Welcome to Subsonic!";
  145.     private static final String DEFAULT_WELCOME_SUBTITLE = null;
  146.     private static final String DEFAULT_WELCOME_MESSAGE = "__Welcome to Subsonic!__\n" +
  147.             "\\\\ \\\\\n" +
  148.             "Subsonic is a free, web-based media streamer, providing ubiquitous access to your music. \n" +
  149.             "\\\\ \\\\\n" +
  150.             "Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple " +
  151.             "players simultaneously, for instance to one player in your kitchen and another in your living room.\n" +
  152.             "\\\\ \\\\\n" +
  153.             "To change or remove this message, log in with administrator rights and go to {link:Settings > General|generalSettings.view}.";
  154.     private static final String DEFAULT_LOGIN_MESSAGE = null;
  155.     private static final String DEFAULT_LOCALE_LANGUAGE = "en";
  156.     private static final String DEFAULT_LOCALE_COUNTRY = "";
  157.     private static final String DEFAULT_LOCALE_VARIANT = "";
  158.     private static final String DEFAULT_THEME_ID = "default";
  159.     private static final int DEFAULT_INDEX_CREATION_INTERVAL = 1;
  160.     private static final int DEFAULT_INDEX_CREATION_HOUR = 3;
  161.     private static final boolean DEFAULT_FAST_CACHE_ENABLED = false;
  162.     private static final int DEFAULT_PODCAST_UPDATE_INTERVAL = 24;
  163.     private static final String DEFAULT_PODCAST_FOLDER = Util.getDefaultPodcastFolder();
  164.     private static final int DEFAULT_PODCAST_EPISODE_RETENTION_COUNT = 10;
  165.     private static final int DEFAULT_PODCAST_EPISODE_DOWNLOAD_COUNT = 1;
  166.     private static final long DEFAULT_DOWNLOAD_BITRATE_LIMIT = 0;
  167.     private static final long DEFAULT_UPLOAD_BITRATE_LIMIT = 0;
  168.     private static final long DEFAULT_STREAM_PORT = 0;
  169.     private static final String DEFAULT_LICENSE_EMAIL = null;
  170.     private static final String DEFAULT_LICENSE_CODE = null;
  171.     private static final String DEFAULT_LICENSE_DATE = null;
  172.     private static final String DEFAULT_DOWNSAMPLING_COMMAND = "ffmpeg -i %s -ab %bk -v 0 -f mp3 -";
  173.     private static final String DEFAULT_HLS_COMMAND = "ffmpeg -ss %o -t %d -i %s -async 1 -b %bk -s %wx%h -ar 44100 -ac 2 -v 0 -f mpegts -vcodec libx264 -preset superfast -acodec libmp3lame -threads 0 -";
  174.     private static final String DEFAULT_JUKEBOX_COMMAND = "ffmpeg -ss %o -i %s -v 0 -f au -";
  175.     private static final boolean DEFAULT_REWRITE_URL = true;
  176.     private static final boolean DEFAULT_LDAP_ENABLED = false;
  177.     private static final String DEFAULT_LDAP_URL = "ldap://host.domain.com:389/cn=Users,dc=domain,dc=com";
  178.     private static final String DEFAULT_LDAP_MANAGER_DN = null;
  179.     private static final String DEFAULT_LDAP_MANAGER_PASSWORD = null;
  180.     private static final String DEFAULT_LDAP_SEARCH_FILTER = "(sAMAccountName={0})";
  181.     private static final boolean DEFAULT_LDAP_AUTO_SHADOWING = false;
  182.     private static final boolean DEFAULT_PORT_FORWARDING_ENABLED = false;
  183.     private static final boolean DEFAULT_GETTING_STARTED_ENABLED = true;
  184.     private static final int DEFAULT_PORT = 80;
  185.     private static final int DEFAULT_HTTPS_PORT = 0;
  186.     private static final boolean DEFAULT_URL_REDIRECTION_ENABLED = false;
  187.     private static final String DEFAULT_URL_REDIRECT_FROM = "yourname";
  188.     private static final String DEFAULT_URL_REDIRECT_CONTEXT_PATH = null;
  189.     private static final String DEFAULT_SERVER_ID = null;
  190.     private static final long DEFAULT_SETTINGS_CHANGED = 0L;
  191.     private static final boolean DEFAULT_ORGANIZE_BY_FOLDER_STRUCTURE = true;
  192.     private static final boolean DEFAULT_SORT_ALBUMS_BY_YEAR = true;
  193.     private static final String DEFAULT_MEDIA_LIBRARY_STATISTICS = "0 0 0 0 0";
  194.     private static final String DEFAULT_TRIAL_EXPIRES = null;
  195.     private static final boolean DEFAULT_DLNA_ENABLED = true;
  196.  
  197.     // Array of obsolete keys.  Used to clean property file.
  198.     private static final List<String> OBSOLETE_KEYS = Arrays.asList("PortForwardingPublicPort", "PortForwardingLocalPort",
  199.             "DownsamplingCommand", "DownsamplingCommand2", "AutoCoverBatch", "MusicMask", "VideoMask", "CoverArtMask, HlsCommand",
  200.             "UrlRedirectTrialExpires", "VideoTrialExpires");
  201.  
  202.     private static final String LOCALES_FILE = "/net/sourceforge/subsonic/i18n/locales.txt";
  203.     private static final String THEMES_FILE = "/net/sourceforge/subsonic/theme/themes.txt";
  204.  
  205.     private static final Logger LOG = Logger.getLogger(SettingsService.class);
  206.  
  207.     private Properties properties = new Properties();
  208.     private List<Theme> themes;
  209.     private List<Locale> locales;
  210.     private InternetRadioDao internetRadioDao;
  211.     private MusicFolderDao musicFolderDao;
  212.     private UserDao userDao;
  213.     private AvatarDao avatarDao;
  214.     private VersionService versionService;
  215.  
  216.     private String[] cachedCoverArtFileTypesArray;
  217.     private String[] cachedMusicFileTypesArray;
  218.     private String[] cachedVideoFileTypesArray;
  219.     private List<MusicFolder> cachedMusicFolders;
  220.    
  221.     private static File subsonicHome;
  222.  
  223.     private boolean licenseValidated = true;
  224.     private Date licenseExpires;
  225.     private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
  226.     private ScheduledFuture<?> licenseValidationFuture;
  227.     private static final long LICENSE_VALIDATION_DELAY_HOURS = 12;
  228.  
  229.     public SettingsService() {
  230.         File propertyFile = getPropertyFile();
  231.  
  232.         if (propertyFile.exists()) {
  233.             FileInputStream in = null;
  234.             try {
  235.                 in = new FileInputStream(propertyFile);
  236.                 properties.load(in);
  237.             } catch (Exception x) {
  238.                 LOG.error("Unable to read from property file.", x);
  239.             } finally {
  240.                 IOUtils.closeQuietly(in);
  241.             }
  242.  
  243.             // Remove obsolete properties.
  244.             for (Iterator<Object> iterator = properties.keySet().iterator(); iterator.hasNext();) {
  245.                 String key = (String) iterator.next();
  246.                 if (OBSOLETE_KEYS.contains(key)) {
  247.                     LOG.debug("Removing obsolete property [" + key + ']');
  248.                     iterator.remove();
  249.                 }
  250.             }
  251.         }
  252.  
  253.         // Start trial.
  254.         if (getTrialExpires() == null) {
  255.             Date expiryDate = new Date(System.currentTimeMillis() + TRIAL_DAYS * 24L * 3600L * 1000L);
  256.             setTrialExpires(expiryDate);
  257.         }
  258.  
  259.         save(false);
  260.     }
  261.  
  262.     /**
  263.      * Register in service locator so that non-Spring objects can access me.
  264.      * This method is invoked automatically by Spring.
  265.      */
  266.     public void init() {
  267.         ServiceLocator.setSettingsService(this);
  268.         scheduleLicenseValidation();
  269.     }
  270.  
  271.     public void save() {
  272.         save(true);
  273.     }
  274.  
  275.     public void save(boolean updateChangedDate) {
  276.         if (updateChangedDate) {
  277.             setProperty(KEY_SETTINGS_CHANGED, String.valueOf(System.currentTimeMillis()));
  278.         }
  279.  
  280.         OutputStream out = null;
  281.         try {
  282.             out = new FileOutputStream(getPropertyFile());
  283.             properties.store(out, "Subsonic preferences.  NOTE: This file is automatically generated.");
  284.         } catch (Exception x) {
  285.             LOG.error("Unable to write to property file.", x);
  286.         } finally {
  287.             IOUtils.closeQuietly(out);
  288.         }
  289.     }
  290.  
  291.     private File getPropertyFile() {
  292.         return new File(getSubsonicHome(), "subsonic.properties");
  293.     }
  294.  
  295.     /**
  296.      * Returns the Subsonic home directory.
  297.      *
  298.      * @return The Subsonic home directory, if it exists.
  299.      * @throws RuntimeException If directory doesn't exist.
  300.      */
  301.     public static synchronized File getSubsonicHome() {
  302.  
  303.         if (subsonicHome != null) {
  304.             return subsonicHome;
  305.         }
  306.  
  307.         File home;
  308.  
  309.         String overrideHome = System.getProperty("subsonic.home");
  310.         if (overrideHome != null) {
  311.             home = new File(overrideHome);
  312.         } else {
  313.             boolean isWindows = System.getProperty("os.name", "Windows").toLowerCase().startsWith("windows");
  314.             home = isWindows ? SUBSONIC_HOME_WINDOWS : SUBSONIC_HOME_OTHER;
  315.         }
  316.  
  317.         // Attempt to create home directory if it doesn't exist.
  318.         if (!home.exists() || !home.isDirectory()) {
  319.             boolean success = home.mkdirs();
  320.             if (success) {
  321.                 subsonicHome = home;
  322.             } else {
  323.                 String message = "The directory " + home + " does not exist. Please create it and make it writable. " +
  324.                         "(You can override the directory location by specifying -Dsubsonic.home=... when " +
  325.                         "starting the servlet container.)";
  326.                 System.err.println("ERROR: " + message);
  327.             }
  328.         } else {
  329.             subsonicHome = home;
  330.         }
  331.  
  332.         return home;
  333.     }
  334.  
  335.     private boolean getBoolean(String key, boolean defaultValue) {
  336.         return Boolean.valueOf(properties.getProperty(key, String.valueOf(defaultValue)));
  337.     }
  338.  
  339.     private void setBoolean(String key, boolean value) {
  340.         setProperty(key, String.valueOf(value));
  341.     }
  342.  
  343.     private String getString(String key, String defaultValue) {
  344.         return properties.getProperty(key, defaultValue);
  345.     }
  346.  
  347.     private void setString(String key, String value) {
  348.         setProperty(key, value);
  349.     }
  350.  
  351.     public String getIndexString() {
  352.         return properties.getProperty(KEY_INDEX_STRING, DEFAULT_INDEX_STRING);
  353.     }
  354.  
  355.     public void setIndexString(String indexString) {
  356.         setProperty(KEY_INDEX_STRING, indexString);
  357.     }
  358.  
  359.     public String getIgnoredArticles() {
  360.         return properties.getProperty(KEY_IGNORED_ARTICLES, DEFAULT_IGNORED_ARTICLES);
  361.     }
  362.  
  363.     public String[] getIgnoredArticlesAsArray() {
  364.         return getIgnoredArticles().split("\\s+");
  365.     }
  366.  
  367.     public void setIgnoredArticles(String ignoredArticles) {
  368.         setProperty(KEY_IGNORED_ARTICLES, ignoredArticles);
  369.     }
  370.  
  371.     public String getShortcuts() {
  372.         return properties.getProperty(KEY_SHORTCUTS, DEFAULT_SHORTCUTS);
  373.     }
  374.  
  375.     public String[] getShortcutsAsArray() {
  376.         return StringUtil.split(getShortcuts());
  377.     }
  378.  
  379.     public void setShortcuts(String shortcuts) {
  380.         setProperty(KEY_SHORTCUTS, shortcuts);
  381.     }
  382.  
  383.     public String getPlaylistFolder() {
  384.         return properties.getProperty(KEY_PLAYLIST_FOLDER, DEFAULT_PLAYLIST_FOLDER);
  385.     }
  386.  
  387.     public void setPlaylistFolder(String playlistFolder) {
  388.         setProperty(KEY_PLAYLIST_FOLDER, playlistFolder);
  389.     }
  390.  
  391.     public String getMusicFileTypes() {
  392.         return properties.getProperty(KEY_MUSIC_FILE_TYPES, DEFAULT_MUSIC_FILE_TYPES);
  393.     }
  394.  
  395.     public synchronized void setMusicFileTypes(String fileTypes) {
  396.         setProperty(KEY_MUSIC_FILE_TYPES, fileTypes);
  397.         cachedMusicFileTypesArray = null;
  398.     }
  399.  
  400.     public synchronized String[] getMusicFileTypesAsArray() {
  401.         if (cachedMusicFileTypesArray == null) {
  402.             cachedMusicFileTypesArray = toStringArray(getMusicFileTypes());
  403.         }
  404.         return cachedMusicFileTypesArray;
  405.     }
  406.  
  407.     public String getVideoFileTypes() {
  408.         return properties.getProperty(KEY_VIDEO_FILE_TYPES, DEFAULT_VIDEO_FILE_TYPES);
  409.     }
  410.  
  411.     public synchronized void setVideoFileTypes(String fileTypes) {
  412.         setProperty(KEY_VIDEO_FILE_TYPES, fileTypes);
  413.         cachedVideoFileTypesArray = null;
  414.     }
  415.  
  416.     public synchronized String[] getVideoFileTypesAsArray() {
  417.         if (cachedVideoFileTypesArray == null) {
  418.             cachedVideoFileTypesArray = toStringArray(getVideoFileTypes());
  419.         }
  420.         return cachedVideoFileTypesArray;
  421.     }
  422.  
  423.     public String getCoverArtFileTypes() {
  424.         return properties.getProperty(KEY_COVER_ART_FILE_TYPES, DEFAULT_COVER_ART_FILE_TYPES);
  425.     }
  426.  
  427.     public synchronized void setCoverArtFileTypes(String fileTypes) {
  428.         setProperty(KEY_COVER_ART_FILE_TYPES, fileTypes);
  429.         cachedCoverArtFileTypesArray = null;
  430.     }
  431.  
  432.     public synchronized String[] getCoverArtFileTypesAsArray() {
  433.         if (cachedCoverArtFileTypesArray == null) {
  434.             cachedCoverArtFileTypesArray = toStringArray(getCoverArtFileTypes());
  435.         }
  436.         return cachedCoverArtFileTypesArray;
  437.     }
  438.  
  439.     public int getCoverArtLimit() {
  440.         return Integer.parseInt(properties.getProperty(KEY_COVER_ART_LIMIT, "" + DEFAULT_COVER_ART_LIMIT));
  441.     }
  442.  
  443.     public void setCoverArtLimit(int limit) {
  444.         setProperty(KEY_COVER_ART_LIMIT, "" + limit);
  445.     }
  446.  
  447.     public String getWelcomeTitle() {
  448.         return StringUtils.trimToNull(properties.getProperty(KEY_WELCOME_TITLE, DEFAULT_WELCOME_TITLE));
  449.     }
  450.  
  451.     public void setWelcomeTitle(String title) {
  452.         setProperty(KEY_WELCOME_TITLE, title);
  453.     }
  454.  
  455.     public String getWelcomeSubtitle() {
  456.         return StringUtils.trimToNull(properties.getProperty(KEY_WELCOME_SUBTITLE, DEFAULT_WELCOME_SUBTITLE));
  457.     }
  458.  
  459.     public void setWelcomeSubtitle(String subtitle) {
  460.         setProperty(KEY_WELCOME_SUBTITLE, subtitle);
  461.     }
  462.  
  463.     public String getWelcomeMessage() {
  464.         return StringUtils.trimToNull(properties.getProperty(KEY_WELCOME_MESSAGE, DEFAULT_WELCOME_MESSAGE));
  465.     }
  466.  
  467.     public void setWelcomeMessage(String message) {
  468.         setProperty(KEY_WELCOME_MESSAGE, message);
  469.     }
  470.  
  471.     public String getLoginMessage() {
  472.         return StringUtils.trimToNull(properties.getProperty(KEY_LOGIN_MESSAGE, DEFAULT_LOGIN_MESSAGE));
  473.     }
  474.  
  475.     public void setLoginMessage(String message) {
  476.         setProperty(KEY_LOGIN_MESSAGE, message);
  477.     }
  478.  
  479.     /**
  480.      * Returns the number of days between automatic index creation, of -1 if automatic index
  481.      * creation is disabled.
  482.      */
  483.     public int getIndexCreationInterval() {
  484.         return Integer.parseInt(properties.getProperty(KEY_INDEX_CREATION_INTERVAL, "" + DEFAULT_INDEX_CREATION_INTERVAL));
  485.     }
  486.  
  487.     /**
  488.      * Sets the number of days between automatic index creation, of -1 if automatic index
  489.      * creation is disabled.
  490.      */
  491.     public void setIndexCreationInterval(int days) {
  492.         setProperty(KEY_INDEX_CREATION_INTERVAL, String.valueOf(days));
  493.     }
  494.  
  495.     /**
  496.      * Returns the hour of day (0 - 23) when automatic index creation should run.
  497.      */
  498.     public int getIndexCreationHour() {
  499.         return Integer.parseInt(properties.getProperty(KEY_INDEX_CREATION_HOUR, String.valueOf(DEFAULT_INDEX_CREATION_HOUR)));
  500.     }
  501.  
  502.     /**
  503.      * Sets the hour of day (0 - 23) when automatic index creation should run.
  504.      */
  505.     public void setIndexCreationHour(int hour) {
  506.         setProperty(KEY_INDEX_CREATION_HOUR, String.valueOf(hour));
  507.     }
  508.  
  509.     public boolean isFastCacheEnabled() {
  510.         return getBoolean(KEY_FAST_CACHE_ENABLED, DEFAULT_FAST_CACHE_ENABLED);
  511.     }
  512.  
  513.     public void setFastCacheEnabled(boolean enabled) {
  514.         setBoolean(KEY_FAST_CACHE_ENABLED, enabled);
  515.     }
  516.  
  517.     /**
  518.      * Returns the number of hours between Podcast updates, of -1 if automatic updates
  519.      * are disabled.
  520.      */
  521.     public int getPodcastUpdateInterval() {
  522.         return Integer.parseInt(properties.getProperty(KEY_PODCAST_UPDATE_INTERVAL, String.valueOf(DEFAULT_PODCAST_UPDATE_INTERVAL)));
  523.     }
  524.  
  525.     /**
  526.      * Sets the number of hours between Podcast updates, of -1 if automatic updates
  527.      * are disabled.
  528.      */
  529.     public void setPodcastUpdateInterval(int hours) {
  530.         setProperty(KEY_PODCAST_UPDATE_INTERVAL, String.valueOf(hours));
  531.     }
  532.  
  533.     /**
  534.      * Returns the number of Podcast episodes to keep (-1 to keep all).
  535.      */
  536.     public int getPodcastEpisodeRetentionCount() {
  537.         return Integer.parseInt(properties.getProperty(KEY_PODCAST_EPISODE_RETENTION_COUNT, String.valueOf(DEFAULT_PODCAST_EPISODE_RETENTION_COUNT)));
  538.     }
  539.  
  540.     /**
  541.      * Sets the number of Podcast episodes to keep (-1 to keep all).
  542.      */
  543.     public void setPodcastEpisodeRetentionCount(int count) {
  544.         setProperty(KEY_PODCAST_EPISODE_RETENTION_COUNT, String.valueOf(count));
  545.     }
  546.  
  547.     /**
  548.      * Returns the number of Podcast episodes to download (-1 to download all).
  549.      */
  550.     public int getPodcastEpisodeDownloadCount() {
  551.         return Integer.parseInt(properties.getProperty(KEY_PODCAST_EPISODE_DOWNLOAD_COUNT, String.valueOf(DEFAULT_PODCAST_EPISODE_DOWNLOAD_COUNT)));
  552.     }
  553.  
  554.     /**
  555.      * Sets the number of Podcast episodes to download (-1 to download all).
  556.      */
  557.     public void setPodcastEpisodeDownloadCount(int count) {
  558.         setProperty(KEY_PODCAST_EPISODE_DOWNLOAD_COUNT, String.valueOf(count));
  559.     }
  560.  
  561.     /**
  562.      * Returns the Podcast download folder.
  563.      */
  564.     public String getPodcastFolder() {
  565.         return properties.getProperty(KEY_PODCAST_FOLDER, DEFAULT_PODCAST_FOLDER);
  566.     }
  567.  
  568.     /**
  569.      * Sets the Podcast download folder.
  570.      */
  571.     public void setPodcastFolder(String folder) {
  572.         setProperty(KEY_PODCAST_FOLDER, folder);
  573.     }
  574.  
  575.     /**
  576.      * @return The download bitrate limit in Kbit/s. Zero if unlimited.
  577.      */
  578.     public long getDownloadBitrateLimit() {
  579.         return Long.parseLong(properties.getProperty(KEY_DOWNLOAD_BITRATE_LIMIT, "" + DEFAULT_DOWNLOAD_BITRATE_LIMIT));
  580.     }
  581.  
  582.     /**
  583.      * @param limit The download bitrate limit in Kbit/s. Zero if unlimited.
  584.      */
  585.     public void setDownloadBitrateLimit(long limit) {
  586.         setProperty(KEY_DOWNLOAD_BITRATE_LIMIT, "" + limit);
  587.     }
  588.  
  589.     /**
  590.      * @return The upload bitrate limit in Kbit/s. Zero if unlimited.
  591.      */
  592.     public long getUploadBitrateLimit() {
  593.         return Long.parseLong(properties.getProperty(KEY_UPLOAD_BITRATE_LIMIT, "" + DEFAULT_UPLOAD_BITRATE_LIMIT));
  594.     }
  595.  
  596.     /**
  597.      * @param limit The upload bitrate limit in Kbit/s. Zero if unlimited.
  598.      */
  599.     public void setUploadBitrateLimit(long limit) {
  600.         setProperty(KEY_UPLOAD_BITRATE_LIMIT, "" + limit);
  601.     }
  602.  
  603.     /**
  604.      * @return The non-SSL stream port. Zero if disabled.
  605.      */
  606.     public int getStreamPort() {
  607.         return Integer.parseInt(properties.getProperty(KEY_STREAM_PORT, "" + DEFAULT_STREAM_PORT));
  608.     }
  609.  
  610.     /**
  611.      * @param port The non-SSL stream port. Zero if disabled.
  612.      */
  613.     public void setStreamPort(int port) {
  614.         setProperty(KEY_STREAM_PORT, "" + port);
  615.     }
  616.  
  617.     public String getLicenseEmail() {
  618.         return properties.getProperty(KEY_LICENSE_EMAIL, DEFAULT_LICENSE_EMAIL);
  619.     }
  620.  
  621.     public void setLicenseEmail(String email) {
  622.         setProperty(KEY_LICENSE_EMAIL, email);
  623.     }
  624.  
  625.     public String getLicenseCode() {
  626.         return properties.getProperty(KEY_LICENSE_CODE, DEFAULT_LICENSE_CODE);
  627.     }
  628.  
  629.     public void setLicenseCode(String code) {
  630.         setProperty(KEY_LICENSE_CODE, code);
  631.     }
  632.  
  633.     public Date getLicenseDate() {
  634.         String value = properties.getProperty(KEY_LICENSE_DATE, DEFAULT_LICENSE_DATE);
  635.         return value == null ? null : new Date(Long.parseLong(value));
  636.     }
  637.  
  638.     public void setLicenseDate(Date date) {
  639.         String value = (date == null ? null : String.valueOf(date.getTime()));
  640.         setProperty(KEY_LICENSE_DATE, value);
  641.     }
  642.  
  643.     public boolean isLicenseValid() {
  644.         return true;
  645.     }
  646.  
  647.     public boolean isLicenseValid(String email, String license) {
  648.         return true;
  649.     }
  650.  
  651.     public LicenseInfo getLicenseInfo() {
  652.         Date trialExpires = getTrialExpires();
  653.         Date now = new Date();
  654.         boolean trialValid = trialExpires.after(now);
  655.         long trialDaysLeft = trialValid ? (trialExpires.getTime() - now.getTime()) / (24L * 3600L * 1000L) : 0L;
  656.  
  657.         return new LicenseInfo(getLicenseEmail(), isLicenseValid(), trialExpires, trialDaysLeft, licenseExpires);
  658.     }
  659.  
  660.     public String getDownsamplingCommand() {
  661.         return properties.getProperty(KEY_DOWNSAMPLING_COMMAND, DEFAULT_DOWNSAMPLING_COMMAND);
  662.     }
  663.  
  664.     public void setDownsamplingCommand(String command) {
  665.         setProperty(KEY_DOWNSAMPLING_COMMAND, command);
  666.     }
  667.  
  668.     public String getHlsCommand() {
  669.         return properties.getProperty(KEY_HLS_COMMAND, DEFAULT_HLS_COMMAND);
  670.     }
  671.  
  672.     public void setHlsCommand(String command) {
  673.         setProperty(KEY_HLS_COMMAND, command);
  674.     }
  675.  
  676.     public String getJukeboxCommand() {
  677.         return properties.getProperty(KEY_JUKEBOX_COMMAND, DEFAULT_JUKEBOX_COMMAND);
  678.     }
  679.  
  680.     public boolean isRewriteUrlEnabled() {
  681.         return getBoolean(KEY_REWRITE_URL, DEFAULT_REWRITE_URL);
  682.     }
  683.  
  684.     public void setRewriteUrlEnabled(boolean rewriteUrl) {
  685.         setBoolean(KEY_REWRITE_URL, rewriteUrl);
  686.     }
  687.  
  688.     public boolean isLdapEnabled() {
  689.         return getBoolean(KEY_LDAP_ENABLED, DEFAULT_LDAP_ENABLED);
  690.     }
  691.  
  692.     public void setLdapEnabled(boolean ldapEnabled) {
  693.         setBoolean(KEY_LDAP_ENABLED, ldapEnabled);
  694.     }
  695.  
  696.     public String getLdapUrl() {
  697.         return properties.getProperty(KEY_LDAP_URL, DEFAULT_LDAP_URL);
  698.     }
  699.  
  700.     public void setLdapUrl(String ldapUrl) {
  701.         properties.setProperty(KEY_LDAP_URL, ldapUrl);
  702.     }
  703.  
  704.     public String getLdapSearchFilter() {
  705.         return properties.getProperty(KEY_LDAP_SEARCH_FILTER, DEFAULT_LDAP_SEARCH_FILTER);
  706.     }
  707.  
  708.     public void setLdapSearchFilter(String ldapSearchFilter) {
  709.         properties.setProperty(KEY_LDAP_SEARCH_FILTER, ldapSearchFilter);
  710.     }
  711.  
  712.     public String getLdapManagerDn() {
  713.         return properties.getProperty(KEY_LDAP_MANAGER_DN, DEFAULT_LDAP_MANAGER_DN);
  714.     }
  715.  
  716.     public void setLdapManagerDn(String ldapManagerDn) {
  717.         properties.setProperty(KEY_LDAP_MANAGER_DN, ldapManagerDn);
  718.     }
  719.  
  720.     public String getLdapManagerPassword() {
  721.         String s = properties.getProperty(KEY_LDAP_MANAGER_PASSWORD, DEFAULT_LDAP_MANAGER_PASSWORD);
  722.         try {
  723.             return StringUtil.utf8HexDecode(s);
  724.         } catch (Exception x) {
  725.             LOG.warn("Failed to decode LDAP manager password.", x);
  726.             return s;
  727.         }
  728.     }
  729.  
  730.     public void setLdapManagerPassword(String ldapManagerPassword) {
  731.         try {
  732.             ldapManagerPassword = StringUtil.utf8HexEncode(ldapManagerPassword);
  733.         } catch (Exception x) {
  734.             LOG.warn("Failed to encode LDAP manager password.", x);
  735.         }
  736.         properties.setProperty(KEY_LDAP_MANAGER_PASSWORD, ldapManagerPassword);
  737.     }
  738.  
  739.     public boolean isLdapAutoShadowing() {
  740.         return getBoolean(KEY_LDAP_AUTO_SHADOWING, DEFAULT_LDAP_AUTO_SHADOWING);
  741.     }
  742.  
  743.     public void setLdapAutoShadowing(boolean ldapAutoShadowing) {
  744.         setBoolean(KEY_LDAP_AUTO_SHADOWING, ldapAutoShadowing);
  745.     }
  746.  
  747.     public boolean isGettingStartedEnabled() {
  748.         return getBoolean(KEY_GETTING_STARTED_ENABLED, DEFAULT_GETTING_STARTED_ENABLED);
  749.     }
  750.  
  751.     public void setGettingStartedEnabled(boolean isGettingStartedEnabled) {
  752.         setBoolean(KEY_GETTING_STARTED_ENABLED, isGettingStartedEnabled);
  753.     }
  754.  
  755.     public boolean isPortForwardingEnabled() {
  756.         return getBoolean(KEY_PORT_FORWARDING_ENABLED, DEFAULT_PORT_FORWARDING_ENABLED);
  757.     }
  758.  
  759.     public void setPortForwardingEnabled(boolean isPortForwardingEnabled) {
  760.         setBoolean(KEY_PORT_FORWARDING_ENABLED, isPortForwardingEnabled);
  761.     }
  762.  
  763.     public int getPort() {
  764.         return Integer.valueOf(properties.getProperty(KEY_PORT, String.valueOf(DEFAULT_PORT)));
  765.     }
  766.  
  767.     public void setPort(int port) {
  768.         setProperty(KEY_PORT, String.valueOf(port));
  769.     }
  770.  
  771.     public int getHttpsPort() {
  772.         return Integer.valueOf(properties.getProperty(KEY_HTTPS_PORT, String.valueOf(DEFAULT_HTTPS_PORT)));
  773.     }
  774.  
  775.     public void setHttpsPort(int httpsPort) {
  776.         setProperty(KEY_HTTPS_PORT, String.valueOf(httpsPort));
  777.     }
  778.  
  779.     public boolean isUrlRedirectionEnabled() {
  780.         return getBoolean(KEY_URL_REDIRECTION_ENABLED, DEFAULT_URL_REDIRECTION_ENABLED);
  781.     }
  782.  
  783.     public void setUrlRedirectionEnabled(boolean isUrlRedirectionEnabled) {
  784.         setBoolean(KEY_URL_REDIRECTION_ENABLED, isUrlRedirectionEnabled);
  785.     }
  786.  
  787.     public String getUrlRedirectFrom() {
  788.         return properties.getProperty(KEY_URL_REDIRECT_FROM, DEFAULT_URL_REDIRECT_FROM);
  789.     }
  790.  
  791.     public void setUrlRedirectFrom(String urlRedirectFrom) {
  792.         properties.setProperty(KEY_URL_REDIRECT_FROM, urlRedirectFrom);
  793.     }
  794.  
  795.     public Date getTrialExpires() {
  796.         String value = properties.getProperty(KEY_TRIAL_EXPIRES, DEFAULT_TRIAL_EXPIRES);
  797.         return value == null ? null : new Date(Long.parseLong(value));
  798.     }
  799.  
  800.     private void setTrialExpires(Date date) {
  801.         String value = (date == null ? null : String.valueOf(date.getTime()));
  802.         setProperty(KEY_TRIAL_EXPIRES, value);
  803.     }
  804.  
  805.     public String getUrlRedirectContextPath() {
  806.         return properties.getProperty(KEY_URL_REDIRECT_CONTEXT_PATH, DEFAULT_URL_REDIRECT_CONTEXT_PATH);
  807.     }
  808.  
  809.     public void setUrlRedirectContextPath(String contextPath) {
  810.         properties.setProperty(KEY_URL_REDIRECT_CONTEXT_PATH, contextPath);
  811.     }
  812.  
  813.     public String getServerId() {
  814.         return properties.getProperty(KEY_SERVER_ID, DEFAULT_SERVER_ID);
  815.     }
  816.  
  817.     public void setServerId(String serverId) {
  818.         properties.setProperty(KEY_SERVER_ID, serverId);
  819.     }
  820.  
  821.     public long getSettingsChanged() {
  822.         return Long.parseLong(properties.getProperty(KEY_SETTINGS_CHANGED, String.valueOf(DEFAULT_SETTINGS_CHANGED)));
  823.     }
  824.  
  825.     public Date getLastScanned() {
  826.         String lastScanned = properties.getProperty(KEY_LAST_SCANNED);
  827.         return lastScanned == null ? null : new Date(Long.parseLong(lastScanned));
  828.     }
  829.  
  830.     public void setLastScanned(Date date) {
  831.         if (date == null) {
  832.             properties.remove(KEY_LAST_SCANNED);
  833.         } else {
  834.             properties.setProperty(KEY_LAST_SCANNED, String.valueOf(date.getTime()));
  835.         }
  836.     }
  837.  
  838.     public boolean isOrganizeByFolderStructure() {
  839.         return getBoolean(KEY_ORGANIZE_BY_FOLDER_STRUCTURE, DEFAULT_ORGANIZE_BY_FOLDER_STRUCTURE);
  840.     }
  841.  
  842.     public void setOrganizeByFolderStructure(boolean b) {
  843.         setBoolean(KEY_ORGANIZE_BY_FOLDER_STRUCTURE, b);
  844.     }
  845.  
  846.     public boolean isSortAlbumsByYear() {
  847.         return getBoolean(KEY_SORT_ALBUMS_BY_YEAR, DEFAULT_SORT_ALBUMS_BY_YEAR);
  848.     }
  849.  
  850.     public void setSortAlbumsByYear(boolean b) {
  851.         setBoolean(KEY_SORT_ALBUMS_BY_YEAR, b);
  852.     }
  853.  
  854.     public MediaLibraryStatistics getMediaLibraryStatistics() {
  855.         return MediaLibraryStatistics.parse(getString(KEY_MEDIA_LIBRARY_STATISTICS, DEFAULT_MEDIA_LIBRARY_STATISTICS));
  856.     }
  857.  
  858.     public void setMediaLibraryStatistics(MediaLibraryStatistics statistics) {
  859.         setString(KEY_MEDIA_LIBRARY_STATISTICS, statistics.format());
  860.     }
  861.  
  862.     /**
  863.      * Returns the locale (for language, date format etc).
  864.      *
  865.      * @return The locale.
  866.      */
  867.     public Locale getLocale() {
  868.         String language = properties.getProperty(KEY_LOCALE_LANGUAGE, DEFAULT_LOCALE_LANGUAGE);
  869.         String country = properties.getProperty(KEY_LOCALE_COUNTRY, DEFAULT_LOCALE_COUNTRY);
  870.         String variant = properties.getProperty(KEY_LOCALE_VARIANT, DEFAULT_LOCALE_VARIANT);
  871.  
  872.         return new Locale(language, country, variant);
  873.     }
  874.  
  875.     /**
  876.      * Sets the locale (for language, date format etc.)
  877.      *
  878.      * @param locale The locale.
  879.      */
  880.     public void setLocale(Locale locale) {
  881.         setProperty(KEY_LOCALE_LANGUAGE, locale.getLanguage());
  882.         setProperty(KEY_LOCALE_COUNTRY, locale.getCountry());
  883.         setProperty(KEY_LOCALE_VARIANT, locale.getVariant());
  884.     }
  885.  
  886.     /**
  887.      * Returns the ID of the theme to use.
  888.      *
  889.      * @return The theme ID.
  890.      */
  891.     public String getThemeId() {
  892.         return properties.getProperty(KEY_THEME_ID, DEFAULT_THEME_ID);
  893.     }
  894.  
  895.     /**
  896.      * Sets the ID of the theme to use.
  897.      *
  898.      * @param themeId The theme ID
  899.      */
  900.     public void setThemeId(String themeId) {
  901.         setProperty(KEY_THEME_ID, themeId);
  902.     }
  903.  
  904.     /**
  905.      * Returns a list of available themes.
  906.      *
  907.      * @return A list of available themes.
  908.      */
  909.     public synchronized Theme[] getAvailableThemes() {
  910.         if (themes == null) {
  911.             themes = new ArrayList<Theme>();
  912.             try {
  913.                 InputStream in = SettingsService.class.getResourceAsStream(THEMES_FILE);
  914.                 String[] lines = StringUtil.readLines(in);
  915.                 for (String line : lines) {
  916.                     String[] elements = StringUtil.split(line);
  917.                     if (elements.length == 2) {
  918.                         themes.add(new Theme(elements[0], elements[1]));
  919.                     } else if (elements.length == 3) {
  920.                         themes.add(new Theme(elements[0], elements[1], elements[2]));
  921.                     } else {
  922.                         LOG.warn("Failed to parse theme from line: [" + line + "].");
  923.                     }
  924.                 }
  925.             } catch (IOException x) {
  926.                 LOG.error("Failed to resolve list of themes.", x);
  927.                 themes.add(new Theme("default", "Subsonic default"));
  928.             }
  929.         }
  930.         return themes.toArray(new Theme[themes.size()]);
  931.     }
  932.  
  933.     /**
  934.      * Returns a list of available locales.
  935.      *
  936.      * @return A list of available locales.
  937.      */
  938.     public synchronized Locale[] getAvailableLocales() {
  939.         if (locales == null) {
  940.             locales = new ArrayList<Locale>();
  941.             try {
  942.                 InputStream in = SettingsService.class.getResourceAsStream(LOCALES_FILE);
  943.                 String[] lines = StringUtil.readLines(in);
  944.  
  945.                 for (String line : lines) {
  946.                     locales.add(parseLocale(line));
  947.                 }
  948.  
  949.             } catch (IOException x) {
  950.                 LOG.error("Failed to resolve list of locales.", x);
  951.                 locales.add(Locale.ENGLISH);
  952.             }
  953.         }
  954.         return locales.toArray(new Locale[locales.size()]);
  955.     }
  956.  
  957.     private Locale parseLocale(String line) {
  958.         String[] s = line.split("_");
  959.         String language = s[0];
  960.         String country = "";
  961.         String variant = "";
  962.  
  963.         if (s.length > 1) {
  964.             country = s[1];
  965.         }
  966.         if (s.length > 2) {
  967.             variant = s[2];
  968.         }
  969.         return new Locale(language, country, variant);
  970.     }
  971.  
  972.     /**
  973.      * Returns the "brand" name. Normally, this is just "Subsonic".
  974.      *
  975.      * @return The brand name.
  976.      */
  977.     public String getBrand() {
  978.         return "Subsonic";
  979.     }
  980.  
  981.     /**
  982.      * Returns all music folders. Non-existing and disabled folders are not included.
  983.      *
  984.      * @return Possibly empty list of all music folders.
  985.      */
  986.     public List<MusicFolder> getAllMusicFolders() {
  987.         return getAllMusicFolders(false, false);
  988.     }
  989.  
  990.     /**
  991.      * Returns all music folders.
  992.      *
  993.      * @param includeDisabled Whether to include disabled folders.
  994.      * @param includeNonExisting Whether to include non-existing folders.
  995.      * @return Possibly empty list of all music folders.
  996.      */
  997.     public List<MusicFolder> getAllMusicFolders(boolean includeDisabled, boolean includeNonExisting) {
  998.         if (cachedMusicFolders == null) {
  999.             cachedMusicFolders = musicFolderDao.getAllMusicFolders();
  1000.         }
  1001.        
  1002.         List<MusicFolder> result = new ArrayList<MusicFolder>(cachedMusicFolders.size());
  1003.         for (MusicFolder folder : cachedMusicFolders) {
  1004.             if ((includeDisabled || folder.isEnabled()) && (includeNonExisting || FileUtil.exists(folder.getPath()))) {
  1005.                 result.add(folder);
  1006.             }
  1007.         }
  1008.         return result;
  1009.     }
  1010.  
  1011.     /**
  1012.      * Returns the music folder with the given ID.
  1013.      *
  1014.      * @param id The ID.
  1015.      * @return The music folder with the given ID, or <code>null</code> if not found.
  1016.      */
  1017.     public MusicFolder getMusicFolderById(Integer id) {
  1018.         List<MusicFolder> all = getAllMusicFolders();
  1019.         for (MusicFolder folder : all) {
  1020.             if (id.equals(folder.getId())) {
  1021.                 return folder;
  1022.             }
  1023.         }
  1024.         return null;
  1025.     }
  1026.  
  1027.     /**
  1028.      * Creates a new music folder.
  1029.      *
  1030.      * @param musicFolder The music folder to create.
  1031.      */
  1032.     public void createMusicFolder(MusicFolder musicFolder) {
  1033.         musicFolderDao.createMusicFolder(musicFolder);
  1034.         cachedMusicFolders = null;
  1035.     }
  1036.  
  1037.     /**
  1038.      * Deletes the music folder with the given ID.
  1039.      *
  1040.      * @param id The ID of the music folder to delete.
  1041.      */
  1042.     public void deleteMusicFolder(Integer id) {
  1043.         musicFolderDao.deleteMusicFolder(id);
  1044.         cachedMusicFolders = null;
  1045.     }
  1046.  
  1047.     /**
  1048.      * Updates the given music folder.
  1049.      *
  1050.      * @param musicFolder The music folder to update.
  1051.      */
  1052.     public void updateMusicFolder(MusicFolder musicFolder) {
  1053.         musicFolderDao.updateMusicFolder(musicFolder);
  1054.         cachedMusicFolders = null;
  1055.     }
  1056.  
  1057.     /**
  1058.      * Returns all internet radio stations. Disabled stations are not returned.
  1059.      *
  1060.      * @return Possibly empty list of all internet radio stations.
  1061.      */
  1062.     public List<InternetRadio> getAllInternetRadios() {
  1063.         return getAllInternetRadios(false);
  1064.     }
  1065.  
  1066.     /**
  1067.      * Returns the internet radio station with the given ID.
  1068.      *
  1069.      * @param id The ID.
  1070.      * @return The internet radio station with the given ID, or <code>null</code> if not found.
  1071.      */
  1072.     public InternetRadio getInternetRadioById(Integer id) {
  1073.         for (InternetRadio radio : getAllInternetRadios()) {
  1074.             if (id.equals(radio.getId())) {
  1075.                 return radio;
  1076.             }
  1077.         }
  1078.         return null;
  1079.     }
  1080.  
  1081.     /**
  1082.      * Returns all internet radio stations.
  1083.      *
  1084.      * @param includeAll Whether disabled stations should be included.
  1085.      * @return Possibly empty list of all internet radio stations.
  1086.      */
  1087.     public List<InternetRadio> getAllInternetRadios(boolean includeAll) {
  1088.         List<InternetRadio> all = internetRadioDao.getAllInternetRadios();
  1089.         List<InternetRadio> result = new ArrayList<InternetRadio>(all.size());
  1090.         for (InternetRadio folder : all) {
  1091.             if (includeAll || folder.isEnabled()) {
  1092.                 result.add(folder);
  1093.             }
  1094.         }
  1095.         return result;
  1096.     }
  1097.  
  1098.     /**
  1099.      * Creates a new internet radio station.
  1100.      *
  1101.      * @param radio The internet radio station to create.
  1102.      */
  1103.     public void createInternetRadio(InternetRadio radio) {
  1104.         internetRadioDao.createInternetRadio(radio);
  1105.     }
  1106.  
  1107.     /**
  1108.      * Deletes the internet radio station with the given ID.
  1109.      *
  1110.      * @param id The internet radio station ID.
  1111.      */
  1112.     public void deleteInternetRadio(Integer id) {
  1113.         internetRadioDao.deleteInternetRadio(id);
  1114.     }
  1115.  
  1116.     /**
  1117.      * Updates the given internet radio station.
  1118.      *
  1119.      * @param radio The internet radio station to update.
  1120.      */
  1121.     public void updateInternetRadio(InternetRadio radio) {
  1122.         internetRadioDao.updateInternetRadio(radio);
  1123.     }
  1124.  
  1125.     /**
  1126.      * Returns settings for the given user.
  1127.      *
  1128.      * @param username The username.
  1129.      * @return User-specific settings. Never <code>null</code>.
  1130.      */
  1131.     public UserSettings getUserSettings(String username) {
  1132.         UserSettings settings = userDao.getUserSettings(username);
  1133.         return settings == null ? createDefaultUserSettings(username) : settings;
  1134.     }
  1135.  
  1136.     private UserSettings createDefaultUserSettings(String username) {
  1137.         UserSettings settings = new UserSettings(username);
  1138.         settings.setFinalVersionNotificationEnabled(true);
  1139.         settings.setBetaVersionNotificationEnabled(false);
  1140.         settings.setShowNowPlayingEnabled(true);
  1141.         settings.setShowChatEnabled(true);
  1142.         settings.setPartyModeEnabled(false);
  1143.         settings.setNowPlayingAllowed(true);
  1144.         settings.setLastFmEnabled(false);
  1145.         settings.setLastFmUsername(null);
  1146.         settings.setLastFmPassword(null);
  1147.         settings.setChanged(new Date());
  1148.  
  1149.         UserSettings.Visibility playlist = settings.getPlaylistVisibility();
  1150.         playlist.setCaptionCutoff(35);
  1151.         playlist.setArtistVisible(true);
  1152.         playlist.setAlbumVisible(true);
  1153.         playlist.setYearVisible(true);
  1154.         playlist.setDurationVisible(true);
  1155.         playlist.setBitRateVisible(true);
  1156.         playlist.setFormatVisible(true);
  1157.         playlist.setFileSizeVisible(true);
  1158.  
  1159.         UserSettings.Visibility main = settings.getMainVisibility();
  1160.         main.setCaptionCutoff(35);
  1161.         main.setTrackNumberVisible(true);
  1162.         main.setArtistVisible(true);
  1163.         main.setDurationVisible(true);
  1164.  
  1165.         return settings;
  1166.     }
  1167.  
  1168.     /**
  1169.      * Updates settings for the given username.
  1170.      *
  1171.      * @param settings The user-specific settings.
  1172.      */
  1173.     public void updateUserSettings(UserSettings settings) {
  1174.         userDao.updateUserSettings(settings);
  1175.     }
  1176.  
  1177.     /**
  1178.      * Returns all system avatars.
  1179.      *
  1180.      * @return All system avatars.
  1181.      */
  1182.     public List<Avatar> getAllSystemAvatars() {
  1183.         return avatarDao.getAllSystemAvatars();
  1184.     }
  1185.  
  1186.     /**
  1187.      * Returns the system avatar with the given ID.
  1188.      *
  1189.      * @param id The system avatar ID.
  1190.      * @return The avatar or <code>null</code> if not found.
  1191.      */
  1192.     public Avatar getSystemAvatar(int id) {
  1193.         return avatarDao.getSystemAvatar(id);
  1194.     }
  1195.  
  1196.     /**
  1197.      * Returns the custom avatar for the given user.
  1198.      *
  1199.      * @param username The username.
  1200.      * @return The avatar or <code>null</code> if not found.
  1201.      */
  1202.     public Avatar getCustomAvatar(String username) {
  1203.         return avatarDao.getCustomAvatar(username);
  1204.     }
  1205.  
  1206.     /**
  1207.      * Sets the custom avatar for the given user.
  1208.      *
  1209.      * @param avatar   The avatar, or <code>null</code> to remove the avatar.
  1210.      * @param username The username.
  1211.      */
  1212.     public void setCustomAvatar(Avatar avatar, String username) {
  1213.         avatarDao.setCustomAvatar(avatar, username);
  1214.     }
  1215.  
  1216.     public boolean isDlnaEnabled() {
  1217.         return getBoolean(KEY_DLNA_ENABLED, DEFAULT_DLNA_ENABLED);
  1218.     }
  1219.  
  1220.     public void setDlnaEnabled(boolean dlnaEnabled) {
  1221.         setBoolean(KEY_DLNA_ENABLED, dlnaEnabled);
  1222.     }
  1223.  
  1224.     private void setProperty(String key, String value) {
  1225.         if (value == null) {
  1226.             properties.remove(key);
  1227.         } else {
  1228.             properties.setProperty(key, value);
  1229.         }
  1230.     }
  1231.  
  1232.     private String[] toStringArray(String s) {
  1233.         List<String> result = new ArrayList<String>();
  1234.         StringTokenizer tokenizer = new StringTokenizer(s, " ");
  1235.         while (tokenizer.hasMoreTokens()) {
  1236.             result.add(tokenizer.nextToken());
  1237.         }
  1238.  
  1239.         return result.toArray(new String[result.size()]);
  1240.     }
  1241.  
  1242.     private void validateLicense() {
  1243.         String email = getLicenseEmail();
  1244.         Date date = getLicenseDate();
  1245.         licenseValidated = true;
  1246.         return;
  1247.     }
  1248.  
  1249.     public synchronized void scheduleLicenseValidation() {
  1250.         if (licenseValidationFuture != null) {
  1251.             licenseValidationFuture.cancel(true);
  1252.         }
  1253.         Runnable task = new Runnable() {
  1254.             public void run() {
  1255.                 validateLicense();
  1256.             }
  1257.         };
  1258.         licenseValidationFuture = executor.scheduleWithFixedDelay(task, 0L, LICENSE_VALIDATION_DELAY_HOURS, TimeUnit.HOURS);
  1259.     }
  1260.  
  1261.     public void setInternetRadioDao(InternetRadioDao internetRadioDao) {
  1262.         this.internetRadioDao = internetRadioDao;
  1263.     }
  1264.  
  1265.     public void setMusicFolderDao(MusicFolderDao musicFolderDao) {
  1266.         this.musicFolderDao = musicFolderDao;
  1267.     }
  1268.  
  1269.     public void setUserDao(UserDao userDao) {
  1270.         this.userDao = userDao;
  1271.     }
  1272.  
  1273.     public void setAvatarDao(AvatarDao avatarDao) {
  1274.         this.avatarDao = avatarDao;
  1275.     }
  1276.  
  1277.     public void setVersionService(VersionService versionService) {
  1278.         this.versionService = versionService;
  1279.     }
  1280. }
clone this paste RAW Paste Data