View Javadoc
1   /*
2    * Copyright (C) 2008-2009, Google Inc.
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.transport;
45  
46  import static org.eclipse.jgit.util.StringUtils.equalsIgnoreCase;
47  import static org.eclipse.jgit.util.StringUtils.toLowerCase;
48  
49  import java.io.File;
50  import java.util.EnumSet;
51  import java.util.HashMap;
52  import java.util.Map;
53  
54  import org.eclipse.jgit.annotations.Nullable;
55  import org.eclipse.jgit.internal.storage.file.LazyObjectIdSetFile;
56  import org.eclipse.jgit.lib.Config;
57  import org.eclipse.jgit.lib.Config.SectionParser;
58  import org.eclipse.jgit.lib.ObjectChecker;
59  import org.eclipse.jgit.lib.ObjectIdSet;
60  import org.eclipse.jgit.lib.Ref;
61  import org.eclipse.jgit.lib.Repository;
62  import org.eclipse.jgit.util.SystemReader;
63  
64  /**
65   * The standard "transfer", "fetch", "protocol", "receive", and "uploadpack"
66   * configuration parameters.
67   */
68  public class TransferConfig {
69  	private static final String FSCK = "fsck"; //$NON-NLS-1$
70  
71  	/** Key for {@link Config#get(SectionParser)}. */
72  	public static final Config.SectionParser<TransferConfig> KEY =
73  			TransferConfig::new;
74  
75  	/**
76  	 * A git configuration value for how to handle a fsck failure of a particular kind.
77  	 * Used in e.g. fsck.missingEmail.
78  	 * @since 4.9
79  	 */
80  	public enum FsckMode {
81  		/**
82  		 * Treat it as an error (the default).
83  		 */
84  		ERROR,
85  		/**
86  		 * Issue a warning (in fact, jgit treats this like IGNORE, but git itself does warn).
87  		 */
88  		WARN,
89  		/**
90  		 * Ignore the error.
91  		 */
92  		IGNORE;
93  	}
94  
95  	/**
96  	 * A git configuration variable for which versions of the Git protocol to prefer.
97  	 * Used in protocol.version.
98  	 */
99  	enum ProtocolVersion {
100 		V0("0"), //$NON-NLS-1$
101 		V2("2"); //$NON-NLS-1$
102 
103 		final String name;
104 
105 		ProtocolVersion(String name) {
106 			this.name = name;
107 		}
108 
109 		@Nullable
110 		static ProtocolVersion parse(@Nullable String name) {
111 			if (name == null) {
112 				return null;
113 			}
114 			for (ProtocolVersion v : ProtocolVersion.values()) {
115 				if (v.name.equals(name)) {
116 					return v;
117 				}
118 			}
119 			return null;
120 		}
121 	}
122 
123 	private final boolean fetchFsck;
124 	private final boolean receiveFsck;
125 	private final String fsckSkipList;
126 	private final EnumSet<ObjectChecker.ErrorType> ignore;
127 	private final boolean allowInvalidPersonIdent;
128 	private final boolean safeForWindows;
129 	private final boolean safeForMacOS;
130 	private final boolean allowRefInWant;
131 	private final boolean allowTipSha1InWant;
132 	private final boolean allowReachableSha1InWant;
133 	private final boolean allowFilter;
134 	private final boolean allowSidebandAll;
135 	private final boolean advertiseSidebandAll;
136 	final @Nullable ProtocolVersion protocolVersion;
137 	final String[] hideRefs;
138 
139 	/**
140 	 * Create a configuration honoring the repository's settings.
141 	 *
142 	 * @param db
143 	 *            the repository to read settings from. The repository is not
144 	 *            retained by the new configuration, instead its settings are
145 	 *            copied during the constructor.
146 	 * @since 5.1.4
147 	 */
148 	public TransferConfig(Repository db) {
149 		this(db.getConfig());
150 	}
151 
152 	/**
153 	 * Create a configuration honoring settings in a
154 	 * {@link org.eclipse.jgit.lib.Config}.
155 	 *
156 	 * @param rc
157 	 *            the source to read settings from. The source is not retained
158 	 *            by the new configuration, instead its settings are copied
159 	 *            during the constructor.
160 	 * @since 5.1.4
161 	 */
162 	@SuppressWarnings("nls")
163 	public TransferConfig(Config rc) {
164 		boolean fsck = rc.getBoolean("transfer", "fsckobjects", false);
165 		fetchFsck = rc.getBoolean("fetch", "fsckobjects", fsck);
166 		receiveFsck = rc.getBoolean("receive", "fsckobjects", fsck);
167 		fsckSkipList = rc.getString(FSCK, null, "skipList");
168 		allowInvalidPersonIdent = rc.getBoolean(FSCK, "allowInvalidPersonIdent",
169 				false);
170 		safeForWindows = rc.getBoolean(FSCK, "safeForWindows",
171 						SystemReader.getInstance().isWindows());
172 		safeForMacOS = rc.getBoolean(FSCK, "safeForMacOS",
173 						SystemReader.getInstance().isMacOS());
174 
175 		ignore = EnumSet.noneOf(ObjectChecker.ErrorType.class);
176 		EnumSet<ObjectChecker.ErrorType> set = EnumSet
177 				.noneOf(ObjectChecker.ErrorType.class);
178 		for (String key : rc.getNames(FSCK)) {
179 			if (equalsIgnoreCase(key, "skipList")
180 					|| equalsIgnoreCase(key, "allowLeadingZeroFileMode")
181 					|| equalsIgnoreCase(key, "allowInvalidPersonIdent")
182 					|| equalsIgnoreCase(key, "safeForWindows")
183 					|| equalsIgnoreCase(key, "safeForMacOS")) {
184 				continue;
185 			}
186 
187 			ObjectChecker.ErrorType id = FsckKeyNameHolder.parse(key);
188 			if (id != null) {
189 				switch (rc.getEnum(FSCK, null, key, FsckMode.ERROR)) {
190 				case ERROR:
191 					ignore.remove(id);
192 					break;
193 				case WARN:
194 				case IGNORE:
195 					ignore.add(id);
196 					break;
197 				}
198 				set.add(id);
199 			}
200 		}
201 		if (!set.contains(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE)
202 				&& rc.getBoolean(FSCK, "allowLeadingZeroFileMode", false)) {
203 			ignore.add(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE);
204 		}
205 
206 		allowRefInWant = rc.getBoolean("uploadpack", "allowrefinwant", false);
207 		allowTipSha1InWant = rc.getBoolean(
208 				"uploadpack", "allowtipsha1inwant", false);
209 		allowReachableSha1InWant = rc.getBoolean(
210 				"uploadpack", "allowreachablesha1inwant", false);
211 		allowFilter = rc.getBoolean(
212 				"uploadpack", "allowfilter", false);
213 		protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version"));
214 		hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
215 		allowSidebandAll = rc.getBoolean(
216 				"uploadpack", "allowsidebandall", false);
217 		advertiseSidebandAll = rc.getBoolean("uploadpack",
218 				"advertisesidebandall", false);
219 	}
220 
221 	/**
222 	 * Create checker to verify fetched objects
223 	 *
224 	 * @return checker to verify fetched objects, or null if checking is not
225 	 *         enabled in the repository configuration.
226 	 * @since 3.6
227 	 */
228 	@Nullable
229 	public ObjectChecker newObjectChecker() {
230 		return newObjectChecker(fetchFsck);
231 	}
232 
233 	/**
234 	 * Create checker to verify objects pushed into this repository
235 	 *
236 	 * @return checker to verify objects pushed into this repository, or null if
237 	 *         checking is not enabled in the repository configuration.
238 	 * @since 4.2
239 	 */
240 	@Nullable
241 	public ObjectChecker newReceiveObjectChecker() {
242 		return newObjectChecker(receiveFsck);
243 	}
244 
245 	private ObjectChecker newObjectChecker(boolean check) {
246 		if (!check) {
247 			return null;
248 		}
249 		return new ObjectChecker()
250 			.setIgnore(ignore)
251 			.setAllowInvalidPersonIdent(allowInvalidPersonIdent)
252 			.setSafeForWindows(safeForWindows)
253 			.setSafeForMacOS(safeForMacOS)
254 			.setSkipList(skipList());
255 	}
256 
257 	private ObjectIdSet skipList() {
258 		if (fsckSkipList != null && !fsckSkipList.isEmpty()) {
259 			return new LazyObjectIdSetFile(new File(fsckSkipList));
260 		}
261 		return null;
262 	}
263 
264 	/**
265 	 * Whether to allow clients to request non-advertised tip SHA-1s
266 	 *
267 	 * @return allow clients to request non-advertised tip SHA-1s?
268 	 * @since 3.1
269 	 */
270 	public boolean isAllowTipSha1InWant() {
271 		return allowTipSha1InWant;
272 	}
273 
274 	/**
275 	 * Whether to allow clients to request non-tip SHA-1s
276 	 *
277 	 * @return allow clients to request non-tip SHA-1s?
278 	 * @since 4.1
279 	 */
280 	public boolean isAllowReachableSha1InWant() {
281 		return allowReachableSha1InWant;
282 	}
283 
284 	/**
285 	 * @return true if clients are allowed to specify a "filter" line
286 	 * @since 5.0
287 	 */
288 	public boolean isAllowFilter() {
289 		return allowFilter;
290 	}
291 
292 	/**
293 	 * @return true if clients are allowed to specify a "want-ref" line
294 	 * @since 5.1
295 	 */
296 	public boolean isAllowRefInWant() {
297 		return allowRefInWant;
298 	}
299 
300 	/**
301 	 * @return true if the server accepts sideband-all requests (see
302 	 *         {{@link #isAdvertiseSidebandAll()} for the advertisement)
303 	 * @since 5.5
304 	 */
305 	public boolean isAllowSidebandAll() {
306 		return allowSidebandAll;
307 	}
308 
309 	/**
310 	 * @return true to advertise sideband all to the clients
311 	 * @since 5.6
312 	 */
313 	public boolean isAdvertiseSidebandAll() {
314 		return advertiseSidebandAll && allowSidebandAll;
315 	}
316 
317 	/**
318 	 * Get {@link org.eclipse.jgit.transport.RefFilter} respecting configured
319 	 * hidden refs.
320 	 *
321 	 * @return {@link org.eclipse.jgit.transport.RefFilter} respecting
322 	 *         configured hidden refs.
323 	 * @since 3.1
324 	 */
325 	public RefFilter getRefFilter() {
326 		if (hideRefs.length == 0)
327 			return RefFilter.DEFAULT;
328 
329 		return new RefFilter() {
330 			@Override
331 			public Map<String, Ref> filter(Map<String, Ref> refs) {
332 				Map<String, Ref> result = new HashMap<>();
333 				for (Map.Entry<String, Ref> e : refs.entrySet()) {
334 					boolean add = true;
335 					for (String hide : hideRefs) {
336 						if (e.getKey().equals(hide) || prefixMatch(hide, e.getKey())) {
337 							add = false;
338 							break;
339 						}
340 					}
341 					if (add)
342 						result.put(e.getKey(), e.getValue());
343 				}
344 				return result;
345 			}
346 
347 			private boolean prefixMatch(String p, String s) {
348 				return p.charAt(p.length() - 1) == '/' && s.startsWith(p);
349 			}
350 		};
351 	}
352 
353 	/**
354 	 * Like {@code getRefFilter() == RefFilter.DEFAULT}, but faster.
355 	 *
356 	 * @return {@code true} if no ref filtering is needed because there
357 	 *         are no configured hidden refs.
358 	 */
359 	boolean hasDefaultRefFilter() {
360 		return hideRefs.length == 0;
361 	}
362 
363 	static class FsckKeyNameHolder {
364 		private static final Map<String, ObjectChecker.ErrorType> errors;
365 
366 		static {
367 			errors = new HashMap<>();
368 			for (ObjectChecker.ErrorType m : ObjectChecker.ErrorType.values()) {
369 				errors.put(keyNameFor(m.name()), m);
370 			}
371 		}
372 
373 		@Nullable
374 		static ObjectChecker.ErrorType parse(String key) {
375 			return errors.get(toLowerCase(key));
376 		}
377 
378 		private static String keyNameFor(String name) {
379 			StringBuilder r = new StringBuilder(name.length());
380 			for (int i = 0; i < name.length(); i++) {
381 				char c = name.charAt(i);
382 				if (c != '_') {
383 					r.append(c);
384 				}
385 			}
386 			return toLowerCase(r.toString());
387 		}
388 
389 		private FsckKeyNameHolder() {
390 		}
391 	}
392 }