mobile/android/base/GeckoProfileDirectories.java

branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
equal deleted inserted replaced
-1:000000000000 0:f8ce2e0fff80
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 package org.mozilla.gecko;
6
7 import java.io.File;
8 import java.util.Enumeration;
9 import java.util.HashMap;
10 import java.util.Map;
11
12 import org.mozilla.gecko.util.INIParser;
13 import org.mozilla.gecko.util.INISection;
14
15 import android.content.Context;
16
17 /**
18 * <code>GeckoProfileDirectories</code> manages access to mappings from profile
19 * names to salted profile directory paths, as well as the default profile name.
20 *
21 * This class will eventually come to encapsulate the remaining logic embedded
22 * in profiles.ini; for now it's a read-only wrapper.
23 */
24 public class GeckoProfileDirectories {
25 @SuppressWarnings("serial")
26 public static class NoMozillaDirectoryException extends Exception {
27 public NoMozillaDirectoryException(Throwable cause) {
28 super(cause);
29 }
30
31 public NoMozillaDirectoryException(String reason) {
32 super(reason);
33 }
34
35 public NoMozillaDirectoryException(String reason, Throwable cause) {
36 super(reason, cause);
37 }
38 }
39
40 @SuppressWarnings("serial")
41 public static class NoSuchProfileException extends Exception {
42 public NoSuchProfileException(String detailMessage, Throwable cause) {
43 super(detailMessage, cause);
44 }
45
46 public NoSuchProfileException(String detailMessage) {
47 super(detailMessage);
48 }
49 }
50
51 private interface INISectionPredicate {
52 public boolean matches(INISection section);
53 }
54
55 private static final String MOZILLA_DIR_NAME = "mozilla";
56
57 /**
58 * Returns true if the supplied profile entry represents the default profile.
59 */
60 private static INISectionPredicate sectionIsDefault = new INISectionPredicate() {
61 @Override
62 public boolean matches(INISection section) {
63 return section.getIntProperty("Default") == 1;
64 }
65 };
66
67 /**
68 * Returns true if the supplied profile entry has a 'Name' field.
69 */
70 private static INISectionPredicate sectionHasName = new INISectionPredicate() {
71 @Override
72 public boolean matches(INISection section) {
73 final String name = section.getStringProperty("Name");
74 return name != null;
75 }
76 };
77
78 /**
79 * Package-scoped because GeckoProfile needs to dig into this in order to do writes.
80 * This will be fixed in Bug 975212.
81 */
82 static INIParser getProfilesINI(File mozillaDir) {
83 return new INIParser(new File(mozillaDir, "profiles.ini"));
84 }
85
86 /**
87 * Utility method to compute a salted profile name: eight random alphanumeric
88 * characters, followed by a period, followed by the profile name.
89 */
90 public static String saltProfileName(final String name) {
91 if (name == null) {
92 throw new IllegalArgumentException("Cannot salt null profile name.");
93 }
94
95 final String allowedChars = "abcdefghijklmnopqrstuvwxyz0123456789";
96 final int scale = allowedChars.length();
97 final int saltSize = 8;
98
99 final StringBuilder saltBuilder = new StringBuilder(saltSize + 1 + name.length());
100 for (int i = 0; i < saltSize; i++) {
101 saltBuilder.append(allowedChars.charAt((int)(Math.random() * scale)));
102 }
103 saltBuilder.append('.');
104 saltBuilder.append(name);
105 return saltBuilder.toString();
106 }
107
108 /**
109 * Return the Mozilla directory within the files directory of the provided
110 * context. This should always be the same within a running application.
111 *
112 * This method is package-scoped so that new {@link GeckoProfile} instances can
113 * contextualize themselves.
114 *
115 * @return a new File object for the Mozilla directory.
116 * @throws NoMozillaDirectoryException
117 * if the directory did not exist and could not be created.
118 */
119 static File getMozillaDirectory(Context context) throws NoMozillaDirectoryException {
120 final File mozillaDir = new File(context.getFilesDir(), MOZILLA_DIR_NAME);
121 if (mozillaDir.exists() || mozillaDir.mkdirs()) {
122 return mozillaDir;
123 }
124
125 // Although this leaks a path to the system log, the path is
126 // predictable (unlike a profile directory), so this is fine.
127 throw new NoMozillaDirectoryException("Unable to create mozilla directory at " + mozillaDir.getAbsolutePath());
128 }
129
130 /**
131 * Discover the default profile name by examining profiles.ini.
132 *
133 * Package-scoped because {@link GeckoProfile} needs access to it.
134 *
135 * @return null if there is no "Default" entry in profiles.ini, or the profile
136 * name if there is.
137 * @throws NoMozillaDirectoryException
138 * if the Mozilla directory did not exist and could not be created.
139 */
140 static String findDefaultProfileName(final Context context) throws NoMozillaDirectoryException {
141 final INIParser parser = GeckoProfileDirectories.getProfilesINI(getMozillaDirectory(context));
142
143 for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) {
144 final INISection section = e.nextElement();
145 if (section.getIntProperty("Default") == 1) {
146 return section.getStringProperty("Name");
147 }
148 }
149
150 return null;
151 }
152
153 static Map<String, String> getDefaultProfile(final File mozillaDir) {
154 return getMatchingProfiles(mozillaDir, sectionIsDefault, true);
155 }
156
157 static Map<String, String> getProfilesNamed(final File mozillaDir, final String name) {
158 final INISectionPredicate predicate = new INISectionPredicate() {
159 @Override
160 public boolean matches(final INISection section) {
161 return name.equals(section.getStringProperty("Name"));
162 }
163 };
164 return getMatchingProfiles(mozillaDir, predicate, true);
165 }
166
167 /**
168 * Calls {@link GeckoProfileDirectories#getMatchingProfiles(File, INISectionPredicate, boolean)}
169 * with a filter to ensure that all profiles are named.
170 */
171 static Map<String, String> getAllProfiles(final File mozillaDir) {
172 return getMatchingProfiles(mozillaDir, sectionHasName, false);
173 }
174
175 /**
176 * Return a mapping from the names of all matching profiles (that is,
177 * profiles appearing in profiles.ini that match the supplied predicate) to
178 * their absolute paths on disk.
179 *
180 * @param mozillaDir
181 * a directory containing profiles.ini.
182 * @param predicate
183 * a predicate to use when evaluating whether to include a
184 * particular INI section.
185 * @param stopOnSuccess
186 * if true, this method will return with the first result that
187 * matches the predicate; if false, all matching results are
188 * included.
189 * @return a {@link Map} from name to path.
190 */
191 public static Map<String, String> getMatchingProfiles(final File mozillaDir, INISectionPredicate predicate, boolean stopOnSuccess) {
192 final HashMap<String, String> result = new HashMap<String, String>();
193 final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
194
195 for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) {
196 final INISection section = e.nextElement();
197 if (predicate == null || predicate.matches(section)) {
198 final String name = section.getStringProperty("Name");
199 final String pathString = section.getStringProperty("Path");
200 final boolean isRelative = section.getIntProperty("IsRelative") == 1;
201 final File path = isRelative ? new File(mozillaDir, pathString) : new File(pathString);
202 result.put(name, path.getAbsolutePath());
203
204 if (stopOnSuccess) {
205 return result;
206 }
207 }
208 }
209 return result;
210 }
211
212 public static File findProfileDir(final File mozillaDir, final String profileName) throws NoSuchProfileException {
213 // Open profiles.ini to find the correct path.
214 final INIParser parser = GeckoProfileDirectories.getProfilesINI(mozillaDir);
215
216 for (Enumeration<INISection> e = parser.getSections().elements(); e.hasMoreElements();) {
217 final INISection section = e.nextElement();
218 final String name = section.getStringProperty("Name");
219 if (name != null && name.equals(profileName)) {
220 if (section.getIntProperty("IsRelative") == 1) {
221 return new File(mozillaDir, section.getStringProperty("Path"));
222 }
223 return new File(section.getStringProperty("Path"));
224 }
225 }
226
227 throw new NoSuchProfileException("No profile " + profileName);
228 }
229 }

mercurial