001/*
002 * Syncany, www.syncany.org
003 * Copyright (C) 2011-2016 Philipp C. Heckel <philipp.heckel@gmail.com>
004 *
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU General Public License as published by
007 * the Free Software Foundation, either version 3 of the License, or
008 * (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
013 * GNU General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
017 */
018package org.syncany.config;
019
020import java.io.File;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.List;
024import java.util.logging.Level;
025import java.util.logging.Logger;
026
027import org.syncany.config.to.DaemonConfigTO;
028import org.syncany.config.to.FolderTO;
029import org.syncany.config.to.UserTO;
030import org.syncany.crypto.CipherUtil;
031import org.syncany.operations.watch.WatchOperationOptions;
032import org.syncany.util.FileUtil;
033
034import com.google.common.base.Predicate;
035import com.google.common.collect.Iterables;
036import com.google.common.primitives.Ints;
037
038/**
039 * The daemon helper provides helper functions to read and/or write the
040 * daemon configuration file as defined by {@link DaemonConfigTO}.
041 *
042 * @author Vincent Wiencek (vwiencek@gmail.com)
043 * @author Philipp C. Heckel (philipp.heckel@gmail.com)
044 */
045public class DaemonConfigHelper {
046        private static final Logger logger = Logger.getLogger(DaemonConfigHelper.class.getSimpleName());
047
048        public static UserTO getFirstDaemonUser(DaemonConfigTO daemonConfig) {
049                List<UserTO> users = readWebSocketServerUsers(daemonConfig);
050
051                if (users.size() > 0) {
052                        return users.get(0);
053                }
054                else {
055                        return null;
056                }
057        }
058
059        /**
060         * Adds the given folder to the user-specific daemon configuration (<code>daemon.xml</code>).
061         *
062         * <p>The method first reads the daemon configuration, checks if the folder is already present
063         * and adds it if it is not. If no daemon config file exists, a new default config file is created
064         * via {@link #createAndWriteDefaultDaemonConfig(File)}. If the folder is already present in
065         * the current daemon config, <code>false</code> is returned. If an error occurs (e.g. an I/O error
066         * or an invalid XML file), a {@link ConfigException} is thrown. If the folder was successfully added,
067         * <code>true</code> is returned.
068         *
069         * @param localDir Absolute path of the local folder to add to the daemon config
070         * @return Returns <code>true</code> if the folder was successfully added to the daemon config,
071         *         <code>false</code> otherwise
072         * @throws ConfigException If an error occurs, e.g. an I/O error or an invalid XML file
073         */
074        public static boolean addFolder(File localDir) throws ConfigException {
075                File daemonConfigFile = new File(UserConfig.getUserConfigDir(), UserConfig.DAEMON_FILE);
076
077                if (daemonConfigFile.exists()) {
078                        DaemonConfigTO daemonConfigTO = DaemonConfigTO.load(daemonConfigFile);
079                        String localDirPath = FileUtil.getCanonicalFile(localDir).getAbsolutePath();
080
081                        // Check if folder already exists
082                        boolean folderExists = false;
083
084                        for (FolderTO folderTO : daemonConfigTO.getFolders()) {
085                                if (localDirPath.equals(folderTO.getPath())) {
086                                        folderExists = true;
087                                        break;
088                                }
089                        }
090
091                        // Add to config if it's not already in there
092                        if (!folderExists) {
093                                logger.log(Level.INFO, "Adding folder to daemon config: " + localDirPath + ", and saving config at " + daemonConfigFile);
094
095                                daemonConfigTO.getFolders().add(new FolderTO(localDirPath));
096                                daemonConfigTO.save(daemonConfigFile);
097
098                                return true;
099                        }
100                        else {
101                                return false;
102                        }
103                }
104                else {
105                        FolderTO localDirFolderTO = new FolderTO(localDir.getAbsolutePath());
106                        createAndWriteDaemonConfig(daemonConfigFile, Arrays.asList(new FolderTO[] { localDirFolderTO }));
107
108                        return true;
109                }
110        }
111        
112        public static boolean removeFolder(File localDir) throws ConfigException {
113                return removeFolder(localDir.getAbsolutePath());
114        }
115        
116        public static boolean removeFolder(String localDirIdentifier) throws ConfigException {
117                File daemonConfigFile = new File(UserConfig.getUserConfigDir(), UserConfig.DAEMON_FILE);
118
119                if (daemonConfigFile.exists()) {
120                        DaemonConfigTO daemonConfigTO = DaemonConfigTO.load(daemonConfigFile);
121                        
122                        // Is index?
123                        Integer localDirIndex = Ints.tryParse(localDirIdentifier);
124                        boolean isLocalDirIndex = localDirIndex != null;
125                        boolean folderRemoved = false;
126                        
127                        // Remove by index
128                        if (isLocalDirIndex) {
129                                localDirIndex--;
130                                
131                                if (localDirIndex >= 0 && localDirIndex < daemonConfigTO.getFolders().size()) {
132                                        logger.log(Level.INFO, "Given identifier (" + localDirIndex + ") is a valid index for " + daemonConfigTO.getFolders().get(localDirIndex).getPath() + ". REMOVING.");
133                                        folderRemoved = null != daemonConfigTO.getFolders().remove((int) localDirIndex);
134                                }
135                                else {
136                                        logger.log(Level.INFO, "Given identifier (" + localDirIndex + ") is a INVALID index. NOT REMOVING.");
137                                }
138                        }
139                        
140                        // Remove by name/path
141                        else {
142                                final String localDirPath = FileUtil.getCanonicalFile(new File(localDirIdentifier)).getAbsolutePath();
143                                
144                                folderRemoved = Iterables.removeIf(daemonConfigTO.getFolders(), new Predicate<FolderTO>() {
145                                        @Override
146                                        public boolean apply(FolderTO folder) {
147                                                return folder.getPath().equals(localDirPath);
148                                        }
149                                });     
150                        }                       
151                                                
152                        // Save (if removed)
153                        if (folderRemoved) {
154                                logger.log(Level.INFO, "Folder was removed. Saving daemon.xml ...");
155
156                                daemonConfigTO.save(daemonConfigFile);
157                                return true;
158                        }
159                        else {
160                                return false;
161                        }
162                }
163                else {
164                        createAndWriteDaemonConfig(daemonConfigFile, Arrays.asList(new FolderTO[] { }));
165                        return true;
166                }
167        }
168
169    public static DaemonConfigTO createAndWriteDefaultDaemonConfig(File daemonConfigFile) throws ConfigException {
170                return createAndWriteDaemonConfig(daemonConfigFile, new ArrayList<FolderTO>());
171        }
172
173    public static DaemonConfigTO createAndWriteExampleDaemonConfig(File daemonConfigFile) throws ConfigException {
174        File defaultFolder = new File(System.getProperty("user.home"), UserConfig.DEFAULT_FOLDER);
175
176        FolderTO defaultFolderTO = new FolderTO();
177        defaultFolderTO.setPath(defaultFolder.getAbsolutePath());
178        defaultFolderTO.setWatchOptions(new WatchOperationOptions());
179
180        return createAndWriteDaemonConfig(daemonConfigFile, Arrays.asList(new FolderTO[] { defaultFolderTO }));
181        }
182
183    public static DaemonConfigTO createAndWriteDaemonConfig(File configFile, List<FolderTO> folders) throws ConfigException {
184        UserTO defaultUserTO = new UserTO();
185                defaultUserTO.setUsername(UserConfig.USER_ADMIN);
186                defaultUserTO.setPassword(CipherUtil.createRandomAlphabeticString(12));
187
188                ArrayList<UserTO> users = new ArrayList<>();
189                users.add(defaultUserTO);
190
191                DaemonConfigTO defaultDaemonConfigTO = new DaemonConfigTO();
192                defaultDaemonConfigTO.setFolders(new ArrayList<>(folders));
193                defaultDaemonConfigTO.setUsers(users);
194
195                defaultDaemonConfigTO.save(configFile);
196
197                return defaultDaemonConfigTO;
198        }
199
200        private static List<UserTO> readWebSocketServerUsers(DaemonConfigTO daemonConfigTO) {
201                List<UserTO> users = daemonConfigTO.getUsers();
202
203                if (users == null) {
204                        users = new ArrayList<UserTO>();
205                }
206
207                // Add CLI credentials
208                if (daemonConfigTO.getPortTO() != null) {
209                        users.add(daemonConfigTO.getPortTO().getUser());
210                }
211
212                return users;
213        }
214}