1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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
66
67
68 public class TransferConfig {
69 private static final String FSCK = "fsck";
70
71
72 public static final Config.SectionParser<TransferConfig> KEY =
73 TransferConfig::new;
74
75
76
77
78
79
80 public enum FsckMode {
81
82
83
84 ERROR,
85
86
87
88 WARN,
89
90
91
92 IGNORE;
93 }
94
95
96
97
98
99 enum ProtocolVersion {
100 V0("0"),
101 V2("2");
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 final @Nullable ProtocolVersion protocolVersion;
136 final String[] hideRefs;
137
138
139
140
141
142
143
144
145
146
147 public TransferConfig(Repository db) {
148 this(db.getConfig());
149 }
150
151
152
153
154
155
156
157
158
159
160
161 @SuppressWarnings("nls")
162 public TransferConfig(Config rc) {
163 boolean fsck = rc.getBoolean("transfer", "fsckobjects", false);
164 fetchFsck = rc.getBoolean("fetch", "fsckobjects", fsck);
165 receiveFsck = rc.getBoolean("receive", "fsckobjects", fsck);
166 fsckSkipList = rc.getString(FSCK, null, "skipList");
167 allowInvalidPersonIdent = rc.getBoolean(FSCK, "allowInvalidPersonIdent",
168 false);
169 safeForWindows = rc.getBoolean(FSCK, "safeForWindows",
170 SystemReader.getInstance().isWindows());
171 safeForMacOS = rc.getBoolean(FSCK, "safeForMacOS",
172 SystemReader.getInstance().isMacOS());
173
174 ignore = EnumSet.noneOf(ObjectChecker.ErrorType.class);
175 EnumSet<ObjectChecker.ErrorType> set = EnumSet
176 .noneOf(ObjectChecker.ErrorType.class);
177 for (String key : rc.getNames(FSCK)) {
178 if (equalsIgnoreCase(key, "skipList")
179 || equalsIgnoreCase(key, "allowLeadingZeroFileMode")
180 || equalsIgnoreCase(key, "allowInvalidPersonIdent")
181 || equalsIgnoreCase(key, "safeForWindows")
182 || equalsIgnoreCase(key, "safeForMacOS")) {
183 continue;
184 }
185
186 ObjectChecker.ErrorType id = FsckKeyNameHolder.parse(key);
187 if (id != null) {
188 switch (rc.getEnum(FSCK, null, key, FsckMode.ERROR)) {
189 case ERROR:
190 ignore.remove(id);
191 break;
192 case WARN:
193 case IGNORE:
194 ignore.add(id);
195 break;
196 }
197 set.add(id);
198 }
199 }
200 if (!set.contains(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE)
201 && rc.getBoolean(FSCK, "allowLeadingZeroFileMode", false)) {
202 ignore.add(ObjectChecker.ErrorType.ZERO_PADDED_FILEMODE);
203 }
204
205 allowRefInWant = rc.getBoolean("uploadpack", "allowrefinwant", false);
206 allowTipSha1InWant = rc.getBoolean(
207 "uploadpack", "allowtipsha1inwant", false);
208 allowReachableSha1InWant = rc.getBoolean(
209 "uploadpack", "allowreachablesha1inwant", false);
210 allowFilter = rc.getBoolean(
211 "uploadpack", "allowfilter", false);
212 protocolVersion = ProtocolVersion.parse(rc.getString("protocol", null, "version"));
213 hideRefs = rc.getStringList("uploadpack", null, "hiderefs");
214 allowSidebandAll = rc.getBoolean(
215 "uploadpack", "allowsidebandall", false);
216 }
217
218
219
220
221
222
223
224
225 @Nullable
226 public ObjectChecker newObjectChecker() {
227 return newObjectChecker(fetchFsck);
228 }
229
230
231
232
233
234
235
236
237 @Nullable
238 public ObjectChecker newReceiveObjectChecker() {
239 return newObjectChecker(receiveFsck);
240 }
241
242 private ObjectChecker newObjectChecker(boolean check) {
243 if (!check) {
244 return null;
245 }
246 return new ObjectChecker()
247 .setIgnore(ignore)
248 .setAllowInvalidPersonIdent(allowInvalidPersonIdent)
249 .setSafeForWindows(safeForWindows)
250 .setSafeForMacOS(safeForMacOS)
251 .setSkipList(skipList());
252 }
253
254 private ObjectIdSet skipList() {
255 if (fsckSkipList != null && !fsckSkipList.isEmpty()) {
256 return new LazyObjectIdSetFile(new File(fsckSkipList));
257 }
258 return null;
259 }
260
261
262
263
264
265
266
267 public boolean isAllowTipSha1InWant() {
268 return allowTipSha1InWant;
269 }
270
271
272
273
274
275
276
277 public boolean isAllowReachableSha1InWant() {
278 return allowReachableSha1InWant;
279 }
280
281
282
283
284
285 public boolean isAllowFilter() {
286 return allowFilter;
287 }
288
289
290
291
292
293 public boolean isAllowRefInWant() {
294 return allowRefInWant;
295 }
296
297
298
299
300
301 public boolean isAllowSidebandAll() {
302 return allowSidebandAll;
303 }
304
305
306
307
308
309
310
311
312
313 public RefFilter getRefFilter() {
314 if (hideRefs.length == 0)
315 return RefFilter.DEFAULT;
316
317 return new RefFilter() {
318 @Override
319 public Map<String, Ref> filter(Map<String, Ref> refs) {
320 Map<String, Ref> result = new HashMap<>();
321 for (Map.Entry<String, Ref> e : refs.entrySet()) {
322 boolean add = true;
323 for (String hide : hideRefs) {
324 if (e.getKey().equals(hide) || prefixMatch(hide, e.getKey())) {
325 add = false;
326 break;
327 }
328 }
329 if (add)
330 result.put(e.getKey(), e.getValue());
331 }
332 return result;
333 }
334
335 private boolean prefixMatch(String p, String s) {
336 return p.charAt(p.length() - 1) == '/' && s.startsWith(p);
337 }
338 };
339 }
340
341
342
343
344
345
346
347 boolean hasDefaultRefFilter() {
348 return hideRefs.length == 0;
349 }
350
351 static class FsckKeyNameHolder {
352 private static final Map<String, ObjectChecker.ErrorType> errors;
353
354 static {
355 errors = new HashMap<>();
356 for (ObjectChecker.ErrorType m : ObjectChecker.ErrorType.values()) {
357 errors.put(keyNameFor(m.name()), m);
358 }
359 }
360
361 @Nullable
362 static ObjectChecker.ErrorType parse(String key) {
363 return errors.get(toLowerCase(key));
364 }
365
366 private static String keyNameFor(String name) {
367 StringBuilder r = new StringBuilder(name.length());
368 for (int i = 0; i < name.length(); i++) {
369 char c = name.charAt(i);
370 if (c != '_') {
371 r.append(c);
372 }
373 }
374 return toLowerCase(r.toString());
375 }
376
377 private FsckKeyNameHolder() {
378 }
379 }
380 }