001package jmri.web.server;
002
003import java.util.ArrayList;
004import java.util.Arrays;
005import java.util.prefs.BackingStoreException;
006import java.util.prefs.Preferences;
007
008import jmri.InstanceManagerAutoDefault;
009import jmri.beans.PreferencesBean;
010import jmri.profile.ProfileManager;
011import jmri.profile.ProfileUtils;
012import org.slf4j.Logger;
013import org.slf4j.LoggerFactory;
014
015/**
016 * @author Randall Wood Copyright (C) 2012, 2017
017 */
018public class WebServerPreferences extends PreferencesBean implements InstanceManagerAutoDefault {
019
020    // preferences elements
021    public static final String DISALLOWED_FRAMES = "disallowedFrames"; // NOI18N
022    public static final String WEB_SERVER_PREFERENCES = "WebServerPreferences"; // NOI18N
023    public static final String FRAME = "frame"; // NOI18N
024    public static final String PORT = "port"; // NOI18N
025    public static final String CLICK_DELAY = "clickDelay"; // NOI18N
026    public static final String REFRESH_DELAY = "refreshDelay"; // NOI18N
027    public static final String USE_AJAX = "useAjax"; // NOI18N
028    public static final String SIMPLE = "simple"; // NOI18N
029    public static final String RAILROAD_NAME = "railroadName"; // NOI18N
030    public static final String ALLOW_REMOTE_CONFIG = "allowRemoteConfig"; // NOI18N
031    public static final String READONLY_POWER = "readonlyPower"; // NOI18N
032    public static final String DISABLE_FRAME_SERVER = "disableFrames"; // NOI18N
033    public static final String REDIRECT_FRAMES = "redirectFramesToPanels"; // NOI18N
034    public static final String USE_ZERO_CONF = "useZeroConf"; // NOI18N
035    private static final String DEFAULT_RAILROAD_NAME = "DefaultRailroadName"; // NOI18N
036
037    // initial defaults if preferences not found
038    private int clickDelay = 1;
039    private int refreshDelay = 5;
040    private boolean useAjax = true;
041    private boolean simple = false;
042    private final ArrayList<String> disallowedFrames =
043            new ArrayList<>(Arrays.asList(Bundle.getMessage("DefaultDisallowedFrames").split(";")));
044    private String railroadName = Bundle.getMessage(DEFAULT_RAILROAD_NAME);
045    private boolean allowRemoteConfig = false;
046    private boolean readonlyPower = true;
047    private int port = 12080;
048    private boolean disableFrames = true;
049    private boolean redirectFramesToPanels = true;
050    private static final Logger log = LoggerFactory.getLogger(WebServerPreferences.class);
051    private boolean useZeroConf = true;
052
053    public WebServerPreferences() {
054        super(ProfileManager.getDefault().getActiveProfile());
055        Preferences sharedPreferences = ProfileUtils.getPreferences(super.getProfile(), this.getClass(), true);
056        this.readPreferences(sharedPreferences);
057    }
058
059    private void readPreferences(Preferences sharedPreferences) {
060        this.allowRemoteConfig = sharedPreferences.getBoolean(ALLOW_REMOTE_CONFIG, this.allowRemoteConfig);
061        this.clickDelay = sharedPreferences.getInt(CLICK_DELAY, this.clickDelay);
062        this.simple = sharedPreferences.getBoolean(SIMPLE, this.simple);
063        this.railroadName = sharedPreferences.get(RAILROAD_NAME, this.railroadName);
064        this.readonlyPower = sharedPreferences.getBoolean(READONLY_POWER, this.readonlyPower);
065        this.refreshDelay = sharedPreferences.getInt(REFRESH_DELAY, this.refreshDelay);
066        this.useAjax = sharedPreferences.getBoolean(USE_AJAX, this.useAjax);
067        this.disableFrames = sharedPreferences.getBoolean(DISABLE_FRAME_SERVER, this.disableFrames);
068        this.redirectFramesToPanels = sharedPreferences.getBoolean(REDIRECT_FRAMES, this.redirectFramesToPanels);
069        try {
070            Preferences frames = sharedPreferences.node(DISALLOWED_FRAMES);
071            if (frames.keys().length != 0) {
072                this.disallowedFrames.clear();
073                for (String key : frames.keys()) { // throws
074                                                   // BackingStoreException
075                    String frame = frames.get(key, null);
076                    if (frame != null && !frame.trim().isEmpty()) {
077                        this.disallowedFrames.add(frame);
078                    }
079                }
080            }
081        } catch (BackingStoreException ex) {
082            // this is expected if sharedPreferences have not been written
083            // previously, so do nothing.
084        }
085        this.port = sharedPreferences.getInt(PORT, this.port);
086        this.useZeroConf = sharedPreferences.getBoolean(USE_ZERO_CONF, this.useZeroConf);
087        this.setIsDirty(false);
088    }
089
090    public void save() {
091        Preferences sharedPreferences = ProfileUtils.getPreferences(this.getProfile(), this.getClass(), true);
092        sharedPreferences.putInt(PORT, this.getPort());
093        sharedPreferences.putBoolean(USE_ZERO_CONF, this.isUseZeroConf());
094        sharedPreferences.putInt(CLICK_DELAY, this.getClickDelay());
095        sharedPreferences.putInt(REFRESH_DELAY, this.getRefreshDelay());
096        sharedPreferences.putBoolean(USE_AJAX, this.isUseAjax());
097        sharedPreferences.putBoolean(SIMPLE, this.isSimple());
098        sharedPreferences.putBoolean(ALLOW_REMOTE_CONFIG, this.allowRemoteConfig());
099        sharedPreferences.putBoolean(READONLY_POWER, this.isReadonlyPower());
100        sharedPreferences.put(RAILROAD_NAME, getRailroadName());
101        sharedPreferences.putBoolean(DISABLE_FRAME_SERVER, this.isDisableFrames());
102        sharedPreferences.putBoolean(REDIRECT_FRAMES, this.redirectFramesToPanels);
103        try {
104            Preferences node = sharedPreferences.node(DISALLOWED_FRAMES);
105            this.disallowedFrames.stream()
106                    .forEach(frame -> node.put(Integer.toString(this.disallowedFrames.indexOf(frame)), frame));
107            if (this.disallowedFrames.size() < node.keys().length) {
108                for (int i = node.keys().length - 1; i >= this.disallowedFrames.size(); i--) {
109                    node.remove(Integer.toString(i));
110                }
111            }
112            sharedPreferences.sync();
113            setIsDirty(false); // Resets only when stored
114        } catch (BackingStoreException ex) {
115            log.error("Exception while saving web server preferences", ex);
116        }
117    }
118
119    public int getClickDelay() {
120        return clickDelay;
121    }
122
123    public void setClickDelay(int value) {
124        int old = this.clickDelay;
125        if (old != value) {
126            this.clickDelay = value;
127            this.firePropertyChange(CLICK_DELAY, old, value);
128        }
129    }
130
131    public int getRefreshDelay() {
132        return refreshDelay;
133    }
134
135    public void setRefreshDelay(int value) {
136        int old = this.refreshDelay;
137        if (old != value) {
138            this.refreshDelay = value;
139            this.firePropertyChange(REFRESH_DELAY, old, value);
140        }
141    }
142
143    public String[] getDisallowedFrames() {
144        return this.disallowedFrames.toArray(new String[this.disallowedFrames.size()]);
145    }
146
147    public boolean isUseAjax() {
148        return useAjax;
149    }
150
151    public void setUseAjax(boolean useAjax) {
152        boolean old = this.useAjax;
153        if (old != useAjax) {
154            this.useAjax = useAjax;
155            this.firePropertyChange(USE_AJAX, old, useAjax);
156        }
157    }
158
159    public boolean isSimple() {
160        return simple;
161    }
162
163    public void setSimple(boolean value) {
164        boolean old = this.simple;
165        if (old != value) {
166            this.simple = value;
167            this.firePropertyChange(SIMPLE, old, value);
168        }
169    }
170
171    public boolean isUseZeroConf() {
172        return useZeroConf;
173    }
174
175    public void setUseZeroConf(boolean value) {
176        boolean old = this.useZeroConf;
177        if (old != value) {
178            this.useZeroConf = value;
179            this.firePropertyChange(USE_ZERO_CONF, old, value);
180        }
181    }
182
183    public boolean allowRemoteConfig() {
184        return this.allowRemoteConfig;
185    }
186
187    public void setAllowRemoteConfig(boolean value) {
188        boolean old = this.allowRemoteConfig;
189        if (old != value) {
190            this.allowRemoteConfig = value;
191            this.firePropertyChange(ALLOW_REMOTE_CONFIG, old, value);
192        }
193    }
194
195    /**
196     * Can the power state be set from web clients?
197     *
198     * @return true if web clients are barred from setting power state; false if
199     *         allowed
200     */
201    public boolean isReadonlyPower() {
202        return readonlyPower;
203    }
204
205    /**
206     * Set if the power state can be set from web clients.
207     *
208     * @param readonlyPower true to bar setting power from web clients; false to
209     *                      allow
210     */
211    public void setReadonlyPower(boolean readonlyPower) {
212        this.readonlyPower = readonlyPower;
213    }
214
215    public void setDisallowedFrames(String[] disallowedFrames) {
216        String[] old = this.getDisallowedFrames();
217        if (!Arrays.equals(old, disallowedFrames)) {
218            this.disallowedFrames.clear();
219            this.disallowedFrames.addAll(Arrays.asList(disallowedFrames));
220            this.firePropertyChange(DISALLOWED_FRAMES, old, disallowedFrames);
221        }
222    }
223
224    public int getPort() {
225        return port;
226    }
227
228    public void setPort(int value) {
229        int old = this.port;
230        if (old != value) {
231            this.port = value;
232            this.firePropertyChange(PORT, old, value);
233            this.setRestartRequired();
234        }
235    }
236
237    /**
238     * Get the name of the railroad.
239     *
240     * @return the railroad name
241     */
242    public String getRailroadName() {
243        return railroadName;
244    }
245
246    /**
247     * Set the railroad name.
248     *
249     * @param railroadName the railroadName to set
250     */
251    public void setRailroadName(String railroadName) {
252        String old = this.railroadName;
253        if ((old != null && !old.equals(railroadName)) || railroadName != null) {
254            if (railroadName != null) {
255                this.railroadName = railroadName;
256            } else {
257                this.railroadName = Bundle.getMessage(DEFAULT_RAILROAD_NAME);
258            }
259            this.firePropertyChange(RAILROAD_NAME, old, this.railroadName);
260        }
261    }
262
263    /**
264     * Test if the railroad name has been set by user.
265     *
266     * @return true if user has not set the railroad name.
267     */
268    public boolean isDefaultRailroadName() {
269        return this.getRailroadName().equals(Bundle.getMessage(DEFAULT_RAILROAD_NAME));
270    }
271
272    /**
273     * Get the default railroad name. This method exists solely to support unit
274     * testing.
275     *
276     * @return The default railroad name
277     */
278    public String getDefaultRailroadName() {
279        return Bundle.getMessage(DEFAULT_RAILROAD_NAME);
280    }
281
282    /**
283     * @return true if displaying frames in web pages is disabled, false
284     *         otherwise
285     */
286    public boolean isDisableFrames() {
287        return disableFrames;
288    }
289
290    /**
291     * Set whether or not frames are returned when requests for frames are made
292     * from web pages.
293     *
294     * @param disableFrames true to prevent frames from being displayed in web
295     *                      pages
296     */
297    public void setDisableFrames(boolean disableFrames) {
298        boolean old = this.disableFrames;
299        if (old != disableFrames) {
300            this.disableFrames = disableFrames;
301            this.firePropertyChange(DISABLE_FRAME_SERVER, old, disableFrames);
302        }
303    }
304
305    /**
306     * Are requests for frames redirected to panels when frames are disabled?
307     *
308     * @return true if frames should be redirected to panels, false otherwise
309     */
310    public boolean isRedirectFramesToPanels() {
311        return redirectFramesToPanels;
312    }
313
314    /**
315     * Set whether or not requests for frames should be redirected to panels
316     * when frames are disabled.
317     *
318     * @param redirectFramesToPanels true if frames should be redirected to
319     *                               panels, false otherwise
320     */
321    public void setRedirectFramesToPanels(boolean redirectFramesToPanels) {
322        boolean old = this.redirectFramesToPanels;
323        if (old != redirectFramesToPanels) {
324            this.redirectFramesToPanels = redirectFramesToPanels;
325            this.firePropertyChange(REDIRECT_FRAMES, old, this.redirectFramesToPanels);
326        }
327    }
328}